常量池:在编译被确定,并保存在.class文件中的一些数据。jvm为每个类维护一个常量池,常量池就是该类型所用到的常量的一个有序集合。在程序执行的时候,常量池存储在方法区中。存在于.class文件中的常量池,在运行期被jvm装载,并且可以扩充。
String.intern()方法就是扩充常量池的一个方法
当一个string实例调用intern方法时,会查找当前常量池中是否已有相同的字符串常量,如果有就返回其引用,如果没有就在常量池中添加对应的字符串,并返回对应字符串常量的引用。
String str0 = "hello world!";
String str1 = new String("hello world!");
String str2 = "hello" + " world!"; //可确定字符串常量池中有3个string字符串常量 "hello" "world!" "hello world!"
Stirng str3 = "hello" + new String("world!"); //常量池1(hello) + 堆2(world + hello world),这是由于String的不可变性所致
System.out.println(str0 == str1); //false
System.out.println(str0 == str2); //true
System.out.println(str0 == str3); //false
str1.intern();
str11 = str1.intern(); //常量池中存在该常量,直接返回其引用
System.out.println(str1 == str11); //false str1在堆中,str11在常量池中
System.out.println(str0 == str11); //true
补充:String的不可变性
//String的实例一旦生成就不会再改变,比如
String str = "he" + "is" + "string";
其中有5个字符串常量:"he", "is", "string", "heis", "heisstring"
因为Stirng 的不可变性产生了很多的临时变量,所以这种情况一般使用StringBuffer或者StringBuider。
jvm优化
jvm会对“+”号连接的字符串常量
优化为连接后的值
String str1 = "aaa";
String str2 = "bbb";
String str3 = "aaabbb";
String str4 = "aaa" + "bbb";
String str5 = "aaa" + str2;
System.out.println(str3 == str4); //true
System.out.println(str3 == str5); //false
对于str5,由于字符串的连接有引用存在,而引用的值在编译期是无法确定的,只有在程序运行的时候动态分配并将其连接后的新引用返回,其实,str3指向常量池,而str5指向堆,所以 str3 == str5 为false
String ab1 = "aaabbb";
final String b = "bbb";
String ab2 = "aaa" + b;
System.out.println(ab1 == ab2); //true
final修饰string就可以确保此时b的值不能被改变,所以jvm也可以对其进行优化。所以归纳一句话为:当String连接符两边是不能被改变的,那在编译时jvm就可以进行优化。final使得b只能指向“bbb”,而“bbb”又是在常量区中(常量区中的常量不能被改变),所以b是不能被改变的,如果上例去掉final,那么b就可以指向其他的地方,表明b是可以被改变的,所以jvm在编译时不进行优化。