如何分析java中字符串缓冲池

java的虚拟机在内存中开辟出一块单独的区域,用来存储字符串对象,这块内存区域被称为字符串缓冲池。那个java的字符串缓冲池是如何工作的呢?

例如下面的代码:

	String a = "abc";
  String b = "abc";
  String c = new String("xyz");

String a = “abc”;创建字符串的时候先查找字符串缓冲池中有没有相同的对象,如果有相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串缓冲池中创建该对象,然后将该对象的应用返回。对于这一步而言,缓冲池中没有"abc"这个字符串对象,所以首先创建一个字符串对象,然后将对象引用返回给a。

String b = “abc”;这一句也是想要创建一个对象,引用变量b使其指向"abc"这一对象。这时,首先查找字符串缓冲池,发现"abc"这个对象已经有了,就直接将这个对象的引用返回给b,此时a和b就共用了一个对象"abc",不过。不用担心a改变了字符串而影响到b,因为字符串都是常量,一旦创建就没办法修改了,除非创建一个新的对象。

String c = new String(“xyz”);java源码中对于String a = new String(”abc”);这种构造方法的实现如下:public String(String original) { this.value = original.value; this.hash = original.hash; }。查找字符串缓冲池发现没有"xyz"这个字符串对象,于是就在字符串缓冲池中创建了一个"xyz"对象,然后再将其引用返回。

从上边的分析可以看出,当new一个字符串时并不一定是创建了一个新的对象,有可能是与别的引用变量共同使用了同一个对象。下面看几个常见的有关字符串缓冲池的问题。

如下语句到底创建了几个字符串对象?

	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”,创建了一个对象。
所以创建的对象的个数分别是:1,0,2,1,1。

怎么判断到底相等不相等呢?

我们在学习java时就知道两个字符串对象相等的判断要用equal而不能使用==,但是学习了字符串缓冲池以后,应该知道为什么不能用==,什么情况下==和equal是等价的,首先,必须知道的是,==比较的是两个对象的内存地址是否相等,下面我们就通过几个程序来看一下:

public static void main(String[] args) { 
 	 String s1 = "Monday"; 
 	 String s2 = "Monday"; 
      if (s1 == s2) 
      	System.out.println("s1 == s2"); 
      else 
      	System.out.println("s1 != s2"); 
}

输出结果:s1 == s2

分析:通过上面对字符串缓冲池的介绍,我们知道s1和s2都是指向字符串缓冲池中的同一个对象,所以内存地址是一样的,即用==可以判断两个字符串是否相等。

public static void main(String[] args) { 
	String s1 = "Monday"; 
	String s2 = new String("Monday"); 
	if (s1 == s2) 
		System.out.println("s1 == s2"); 
	else 
		System.out.println("s1 != s2"); 
	if (s1.equals(s2)) 
		System.out.println("s1 equals s2"); 
	else 
		System.out.println("s1 not equals s2");
}

输出结果:s1 != s2;s1 equals s2

分析:由上边的分析我们知道,String s2 = new String(“Monday”);这一句话没有在字符串缓冲池中创建新的对象,但是会在内存的其他位置创建一个新的对象,所以s1是指向字符串缓冲池的,s2是指向内存的其他位置,两者的内存地址不同的。

public static void main(String[] args) { 
	String s1 = "Monday"; 
	String s2 = new String("Monday"); 
	s2 = s2.intern();
	if (s1 == s2) 
		System.out.println("s1 == s2"); 
 	else 
	 	System.out.println("s1 != s2"); 
 	if (s1.equals(s2)) 
 		System.out.println("s1 equals s2");
  	else 
  		System.out.println("s1 not equals s2"); 
 }

输出结果:s1 == s2;s1 equals s2

分析:先来说说intern()这个方法的作用吧,这个方法的作用是返回在字符串缓冲池中的对象的引用,所以s2指向的也是字符串缓冲池中的地址,和s1是相等的。

public static void main(String[] args) { 
	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是一个变量,所以不能提前确定他的值,所以两者不相等。从这个例子我们可以看出,只有+连接的两边都是字符串常量时,引用才会指向字符串缓冲池,否则都是指向内存中的其他地址。

public static void main(String[] args) { 
	String Monday = "Monday"; 
	String Mon = "Mon"; 
	final String day = "day"; 
	System.out.println(Monday == "Mon" + "day"); 
	System.out.println(Monday == "Mon" + day); 
}

输出结果:true;true

分析:如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量来使用,也就是说在用到该final变量的地方,相当于直接访问这个常量,不需要在运行时确定。所以第二句的引用也是指向的字符串缓冲池。

public static void main(String[] args) { 
	String Monday = "Monday"; 
	String Mon = "Mon"; 
	final String day;
	day = "day"; 
	System.out.println(Monday == "Mon" + "day"); 
	System.out.println(Monday == "Mon" + day); 
}

输出结果:true;false
  
分析:只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,上面的这段代码就不会进行优化。

发布了21 篇原创文章 · 获赞 10 · 访问量 8099

猜你喜欢

转载自blog.csdn.net/weixin_44997483/article/details/102591223