Java中的字符串缓冲池——String pool

在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也变成了常量,所以第二句的引用也是指向的字符串缓冲池。



你可能感兴趣的:(Java中的字符串缓冲池——String pool)