前言
以1.7后的版本为例,String.intern():
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
这段是对intern()方法的源码注释,大概翻译为两点:
- intern()表示先在常量池中查询字符串是否存在,存在的依据是用equals()方法判断,相等则表示存在。如果存在(true),则返回该字符串(then the string from the pool is returned);
- 如果字符串不存在,则将该字符串对象添加到常量池中,并返回该字符串的引用(a reference to this object is returned)
案例分析
// 案例一
String s1 = new String("abc");
System.out.println(s1 == s1.intern());//false
案例一分析:
new String(“abc”):做了2件事:
1. ""表示在常量池中开辟了一个地址,存入abc;
2. new String()表示在堆空间中开辟了一个地址,存入abc;
- s1是堆空间地址,s1.intern()先判断常量池有没有"abc",发现有,ok直接返回该地址,因此s1.intern()是常量池的地址
// 案例二
String s1 = new String("a") + new String("bc");
System.out.println(s1 == s1.intern());//true
案例二分析
new String(“a”) + new String(“bc”):做了3件事:
(篇幅有限,同一个空间创建算1件事)
1.""表示常量池创建a,常量池创建bc;
2. new String()堆空间创建a,堆空间创建bc
3.+表示堆空间创建abc
- 所以s1是堆空间地址,s1.intern()先去常量池找abc,没有,ok创建abc对象,返回该对象的引用。
- 注意1.7后由于堆已有abc,所以创建abc对象相当于将堆空间的地址拷贝一份到常量池中。
- 因此,s1.intern()表示堆空间的地址
// 案例三
String s1 = new String("a") + "bc";
System.out.println(s1 == s1.intern());//true
案例三分析
根据案例二的分析,做了3件事
1.字符串a在堆空间和常量池各创建一份,
2.而bc很明显只有常量池创建,
3.+则表示堆空间创建了abc
- 因此,s1表示堆空间地址,s1.intern()发现常量池没有abc,则将堆空间地址拷贝一份到常量池,所以s1.intern()表示堆空间的地址
// 案例四
String s1 = new String("a" + "b");
String s2 = "ab";
System.out.println(s1 == s2);//false
System.out.println(s1.intern() == s2);//true
System.out.println(s1 == s1.intern());//false
案例四分析
1.常量池创建a,创建b,
2.+则常量池创建ab,
3.new String()则堆空间创建ab
- 这样就很明显了,s1表示堆空间地址,s2表示常量池地址
- s1.intern()先判断常量池有没有ab,发现有,ok,返回常量池中已存在的ab地址
- 因此s1.intern()表示常量池地址
// 案例五
/**
*注意:用append()拼接字符串和用 + new StringBuilder()拼接字符串,对结果并没有影响,无非是堆中有没有创建对象的区别
*/
String t1 = new StringBuilder("ab") + new StringBuilder("c").toString();
//String t1 = new StringBuilder("ab").append("c").toString();
System.out(t1 == t1.intern()); //true
String s1 = new StringBuilder("j") + new StringBuilder("ava").toString();
//String s1 = new StringBuilder("j").append("ava").toString();
System.out.println(s1 == s1.intern()); //false
案例五分析
必须说下这个案例贼坑爹!!!
先分析t1:
1.new StringBuilder()在堆空间创建,t1表示堆空间的地址,存着abc,常量池不存在abc;
2.所以t1.intern()会在常量池拷贝一份堆空间的地址过去,因此地址一样,true
再分析s1:
1.new StringBuilder()在堆空间创建,s1表示堆空间的地址,存着java字符串;
2.但是这个时候常量池已经存在java字符串了!!!注意之所以存在,是因为"java"这个字符串特殊
3.原因:当你运行这个方法时,会加载sun.misc.Version这个类,这个类会初始化java这个字符串,所以java这个字符串已经存在了,
4.基于这个原因,返回false
多说一句,这个点之前真没有注意,确实也是在B站看视频面试题的过程中了解到的,
另外受到版本影响,有可能初始化的不是这个字符串。当然面试如果遇到了,默认就是java字符串
总结:
- ""表示常量池创建字符串对象,new的方式表示堆中创建字符串,
- " " + " "会在常量池创建字符串的连接
以上就是本人对String的==理解,如果有错,请指出来,以免误人子弟,谢谢