在thingking In Java 中这样说到:
String manipulation is arguably one of the most common activities in computer programming.
字符串操作是计算机程序设计中最常见的行为。
java的虚拟机在内存中开辟出一块单独的区域,用来存储字符串对象,这块内存区域被称为字符串缓冲池。(应该是为了节约计算机资源)
java的字符串缓冲池是如何工作的呢?
例如代码:String str1 = "aaa";
创建字符串的时候先查找字符串缓冲池中有没有相同的对象,如果有相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串缓冲池中创建该对象,然后将该对象的应用返回。对于这一步而言,缓冲池中没有aaa这个字符串对象,所以首先创建一个字符串对象,然后将对象引用返回给str。
String str2 = "aaa";
对于这一句,也是想要创建一个对象引用变量str2使其指向aaa这一对象。这时,首先查找字符串缓冲池,发现aaa这个对象已经有了,这是就直接将这个对象的引用返回给str2,此时str1和str2就共用了一个对象aaa,这时会出现一个疑问,两个变量引用同一个对象,此时,str1改变了这个对象,str回不回受影响呢?不用担心,因为字符串都是常量,一旦创建就没办法修改了,除非创建一个新的对象。
所有的String对象都是不可变对象,所有的看似改变String对象的操作都只不过是创建了一个新的String对象,而原先的对象丝毫未变。如:
String str1 = "aaa"; String str2 = "aaa"; System.out.println(str1.toUpperCase());//AAA str1 += "bbb"; System.out.println(str1);//aaabbb System.out.println(str2);//aaa
输出结果为:
AAA
aaabbb
aaa
可以发现无论是转换成大写操作还是拼接操作都没有改变str2的引用值。
继续代码:
String str3 = new String("aaa");
String str4 = new String("ccc");
上面中的代码执行时:
第一句:先判断字符串缓冲池中是否有"aaa"对象,此时已经存在,不再创建,只是将“aaa”的引用返回给str3,此句创建了一个对象。注意,str2 不是对象,只是引用.只有 new 生成的才是对象.
第二句:先判断字符串缓冲池中是否有"ccc"对象,此时不存在,创建“ccc”对象,再将“ccc”的引用返回给str4。
String a = "abc"; String b = "abc"; String c = new String("xyz"); String d = new String("xyz"); String e="ab"+"cd";
这个程序与上边的程序比较相似:
String a = “abc”;这一句由于缓冲池中没有abc这个字符串对象,所以会创建一个对象;String b = “abc”;由于缓冲池中已经有了abc这个对象,所以不会再创建新的对象;String c = new String(“xyz”);由于没有xyz这个字符串对象,所以会首先创建一个xyz的对象,然后这个字符串对象由作为String的构造方法,在内存中(不是缓冲池中)又创建了一个新的字符串对象,所以一共创建了两个对象;String d = new String(“xyz”);省略了创建一个对象的过程,所以只创建了一个对象;String e=”ab”+”cd”;由于常量的值在编译的时候就被确定了。所以这一句等价于String e=”abcd”;创建了一个对象。
在学习java时就知道两个字符串对象相等的判断要用equal而不能使用==,但是学习了字符串缓冲池以后,应该知道为什么不能用==,什么情况下==和equal是等价的,首先,必须知道的是,==比较的是两个对象的内存地址是否相等。
String a = "abc"; String b = "abc"; if(a==b){ System.out.println("a==b"); }else{ System.out.println("a!=b"); }
输出结果为: a==b
此时使用==可以进行比较的原因就是a和b引用的是字符串缓冲池中的同一个对象,他们的物理地址相同,所以值是相同的。
String a = "abc"; String c = new String("abc"); if(a==c){ System.out.println("a==c"); }else{ System.out.println("a!=c"); } if(a.equals(c)){ System.out.println("a.equals(c)"); }else{ System.out.println("!a.equals(c)"); }
结果为:
a!=c a.equals(c)
String c = new String("abc");这一句话没有在字符串缓冲池中创建新的对象,但是会在内存的其他位置创建一个新的对象,所以a是指向字符串缓冲池的,c是指向内存的其他位置,两者的内存地址不同的。
String a = "abc"; String c = new String("abc"); c = c.intern(); if(a==c){ System.out.println("a==c"); }else{ System.out.println("a!=c"); } if(a.equals(c)){ System.out.println("a.equals(c)"); }else{ System.out.println("!a.equals(c)"); }
在代码中增加了 c = c.intern();语句后,结果为:
a==c a.equals(c)
intern()方法的作用是返回在字符串缓冲池中的对象的引用,所以c指向的也是字符串缓冲池中的地址,和a是相等的。
String Monday = "Monday"; String Mon = "Mon"; String day = "day"; System.out.println(Monday == "Mon" + "day"); System.out.println(Monday == "Mon" + day);
结果为:
true
false
第一个为true ,因为两者都是常量所以在编译阶段就已经能确定了,在第二个中,day是一个变量,所以不能提前确定他的值,所以两者不相等,从这个例子我们可以看出,只有+连接的两边都是字符串常量时,引用才会指向字符串缓冲池,否则指向内存中的其他地址。
String Monday = "Monday"; String Mon = "Mon"; final String day = "day"; System.out.println(Monday == "Mon" + "day"); System.out.println(Monday == "Mon" + day);
加上final后day也变成了常量,所以第二句的引用也是指向的字符串缓冲池。