黑马程序员技术交流社区
标题:
String,StringBuffer,StringBuilder及其字符串相关种种
[打印本页]
作者:
常万
时间:
2012-4-20 18:25
标题:
String,StringBuffer,StringBuilder及其字符串相关种种
String,StringBuffer,StringBuilder及其字符串相关种种 相信对看完视频还有点迷糊的人有帮助{:soso_e163:}
这三个类都在java.lang包里面。
1.String
String 类代表字符串。Java 程序中的所有字符串字面值(如"abc")都作为此类的实例实现。
字符串是常量,它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
例如String str = "abc"; 等效于char data[] = {'a', 'b', 'c'}; String str = new String(data); 在constant pool里面共享"abc"。
记住String是不可变的,如果发生了改变一定是创建了新的对象。
看例子。String str="a"+"b"+"c"+"a";在constant pool中存在那些的字符串常量呢?答案是六个。首先有三个直接常量"a"、"b"、"c",其中两次出现的"a"是共享的。此外,由"a"+"b"生成"ab" 保存在内存中,接着"ab"+"c"生成"abc"保存在内存中,最后由"abc"+"a"生成"abca"保存在内存中,所以常量池中一共存在六个字符串常量。
正因为string类是不可变的,同时也产生很多额外的临时变量,所以才引发了stringbuffer和stringbuilder这两个类来避免这个问题。
2.StringBuffer与StringBuilder
StringBuffer是线程安全的可变字符序列。StringBuilder也是可变序列,但是不保证同步。它被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。
3.Java堆、栈和常量池
首先了解各种存储类型。
(1)寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制。
(2)栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。)
(3)堆:存放所有new出来的对象。
(4)静态域:存放静态成员(static定义的)。
(5)常量池:存放字符串常量和基本类型常量。
栈和常量池中的对象可以共享,堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
对于字符串,其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
以下代码的存储示意图:
String s1 = "china";
String s2 = "china";
String s3 = "china";
String ss1 = new String("china");
String ss2 = new String("china");
String ss3 = new String("china");
对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
int i1 = 9;
int i2 = 9;
int i3 = 9;
public static final int INT1 = 9;
public static final int INT2 = 9;
public static final int INT3 = 9;
对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
4.关于字符串的比较
一直很纠结...
首先弄清"=="与"equals"。
"=="通常用在基本类型的值的比较中,比如int i1=1,i2=1;i1==i2的结果为ture是毫无疑问的。可是在String比较时常常让人弄晕乎晕乎,曾经我以为自己对此了如指掌,可是若干年后...哈哈,现在没问题啦~且听分析,当"=="用于对象的比较时,比较的是对象的引用,记住是引用!(可以看看《java编程思想》第4版第3章3.7.1测试对象的行等价性)
从上面我们已经知道String有两种对象,一种是编译创建保存在常量池中的常量对象(不使用new,直接赋值),一种是运行期通过new创建的对象。保存在常量池中的对象是可以共享的,也就是说相同的内容只保存一份,从上面的图也可以看出来。所以对象字符串常量对象,只要内容相同,那么它们引用的就是同一个对象,所以用"=="比较两个内容相同的字符串常量对象,当然是相同咯。而new的对象每个都是各自不同的,即使内容相同,也在堆上保存着不同的副本(见上图),引用自然不一样咯~
接着来说equals。equals是类Object的方法,Object类的equals方法实现对象上差别可能性最大的相等关系,对于任何非空引用值x和y,当且仅当x和y引用同一个对象时,此方法才返回true(x==y具有值true)。其实Object类中equals的实现是这样的:
boolean equals(Object o) { return this==o;}
因为任何类都继承自Object类,所以没有重写equals方法的类都将作用这个实现,也就是说,对于没有重写equals的类,用equals比较该类型的对象与用"=="是一样的。而String类重写了eqauls方法,覆盖equals的原始实现。在String类中,equals方法将此字符串与指定的对象比较,当且仅当该参数不为 null,并且是与此对象表示相同字符序列的String对象时,结果才为true。所以说equals比较的是String的“内容”。
下面来看一点代码,从此应该彻底不会被迷惑了吧~
public class StringCompare
{
public static void main(String[] args)
{
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);//true,引用常量池中相同的对象
System.out.println(s1.equals(s2));//true,都是"abc"
String ss1 = new String("abc");
String ss2 = new String("abc");
System.out.println(ss1==ss2);//false,在堆上ss1、ss2是两个不同的对象
System.out.println(ss1.equals(ss2));//true,都是"abc"
StringBuffer sb1=new StringBuffer("abc");
StringBuffer sb2=new StringBuffer("abc");
System.out.println(sb1==sb2);
System.out.println(sb1.equals(sb2));
//两个都是false,因为StringBuffer没有重写equals
StringBuilder si1=new StringBuilder("abc");
StringBuilder si2=new StringBuilder("abc");
System.out.println(si1==si2);
System.out.println(si1.equals(si2));
//两个都是false,因为StringBuilder没有重写equals
}
}
这下弄清楚咯~
查看一下jdk api的文档会看到除了String外,另外两个表示字条串的类都没有重写equals,而是继承Object的equals。还有,这三个类都是final类,不能被继承。
作者:
胡文杰
时间:
2012-4-20 18:26
百度过来怎么把图片也过来呢!
作者:
常万
时间:
2012-4-20 18:31
自己添加图片啊....呵呵
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2