**
intern()方法
**
(一)new String在常量池中生成字符串,在堆上创建字符串对象。
String s = newString("1"),生成了常量池中的“1” 和堆空间中的字符串对象。
s.intern();
当调用intern()方法时,s对象去常量池中寻找后发现"1"已经存在于常量池中了(s的引用地址是堆中指向常量池)。
(二)通过字面赋值创建字符串
String s2 = "1",这行代码是生成一个s2的引用指向常量池中的“1”对象。
先在常量池中查找是否存在,若存在,则将栈中的引用直接指向该字符串;若不存在,则会在常量池中生成一个字符串,再将栈中的引用直接指向该字符串。
举例1
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
输出:false 原因: s 和 s2 的引用地址明显不同。因此返回了false。
举例2
String s3 = new String("1") + new String("1");
//在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象
//(内容为"11")。注意此时常量池中是没有 “11”对象的
s3.intern();
//将s3中的“11”字符串放入 String常量池中,此时常量池中
//不存在“11”字符串
//JDK1.6的做法是直接在常量池中生成一个 "11" 的对象
//JDK1.7直接存储堆中的引用。
//这份引用直接指向 s3 引用的对象,
//也就是说s3.intern() == s3会返回true。
String s4 = "11";
System.out.println(s3 == s4);
输出:JDK1.6及以下:fasle
JDK1.7及以上:true
(三)常量字符的 “+” 操作,会直接在编译阶段,将字符串合并,如:
String = ‘‘str11’’ + "333’’, 在编译阶段会直接将字符串合并为String = ‘‘str11333’’,于是会去常量池中查找是否存在’‘str11333’’,从而进行创建或者引用。
(四)对于final字段,编译器直接进行了常量替换,
(非final字段,则进行了赋值)
final String str1 = ‘‘ja’’;
final String str2 = ‘‘va’’;
String str3 = str1 + str2;
在编译时,直接替换成了String str3=”ja”+”va”,
根据第三条规则,再次替换成String str3=”JAVA”
(五)常量字符串和变量拼接时(如:String str3=baseStr + “01”;)
会调用stringBuilder.append()在堆上创建新的对象
(六)JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中字符串的引用,这一点与之前没有区别:
区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。
简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。
举例3:
String s = new String("1");
String s2 = "1";
s.intern();
System.out.println(s == s2);
String s = newString(“1”),生成了常量池中的“1” 和堆空间中的字符串对象。
String s2 = “1”,这行代码是生成一个s2的引用指向常量池中的“1”对象,但是发现已经存在了,那么就直接指向了它。
s.intern(),这一行在这里就没什么实际作用了。因为"1"已经存在了。
结果就是 s 和 s2 的引用地址明显不同。因此返回了false
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);
String s3 = new String(“1”) + newString(“1”),这行代码在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中是没有 “11”对象的。
String s4 = “11”, 这一行代码会直接去生成常量池中的"11"。
s3.intern(),这一行在这里就没什么实际作用了。因为"11"已经存在了。
结果就是 s3 和 s4 的引用地址明显不同。因此返回了false