jdk:1.8
今天再看极客学院关于《JVM自动内存管理:内存区域基础概念》
有这么一块没有看懂:
public class RuntimeConstantPoolChange {
public static void main(String[] args) {
// 第一段代码
String str1 = new StringBuilder("极客").append("学院").toString();
System.out.println(str1.intern() == str1);
// 第二段代码
String str2 = new StringBuilder("极客").toString();
System.out.println(str2.intern() == str2);
}
}
结果:
true
false
这段代码中的str2
是着实没有看懂为什么为false。
经过在网上查阅各种博客后,终于明白了,以此记录下。
我们先看第一段代码:
String str1 = new StringBuilder("极客").append("学院").toString();
System.out.println(str1.intern() == str1);
我们先看第一段代码
①因为极客
与学院
,是用双引号声明,所以会在常量池中创建两个相应的字符串对象。
②由于使用new,所以会在java heap(堆)中创建一个内容为极客学院
的字符串对象(暂时命名为s1,方便后面使用),并返回在堆中的引用。
当调用str1.intern()
的时候,第一步会去常量池中去找是否有极客学院
的字符串。要是没有,则就在常量池中记录Java Heap中首次出现的该字符串的引用,并返回该引用。
也就是说这时常量池中保存的引用是我们之前在java heap(堆)中创建的那个s1
。所以实际效果为s1 == str1
,这里的s1
就是str1
,这也就是为什么str1.intern()==str1
为true的原因。
我们再来看第二段:
String str2 = new StringBuilder("极客").toString();
System.out.println(str2.intern() == str2);
首先同样是:
①由于字符串极客
使用双引号声明。所以会去常量池中看是否已经存在(若不存在,则创建一个字符串对象),由于在第一段代码中已经存在了,所以这时什么也不会干。
②又由于使用new ,所以会在java heap(堆)中创建一个内容相同的String对象。然后返回堆中String
对象的引用。
也就是说第二段代码分别在常量池和堆中生成了两个内容相同的String对象。
(由于第一段代码中已经创建过了,所以这里只会有在堆中创建String对象的操作)
再执行str2.intern() == str2
时,str2.intern()
返回的是第一段代码s1
的引用。实际效果是str1 == str2
,这肯定会返回false,因为str1和str2只是内容相同在堆中new出来的两个String对象。其引用肯定是不同的!
String.intern()是一个Native方法,它的作用是:如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此String内容相同的字符串,并返回常量池中创建的字符串的引用。
当常量池中没有该字符串时,JDK7的intern()方法的实现不再是在常量池中创建与此String内容相同的字符串,而改为在常量池中记录Java Heap中首次出现的该字符串的引用,并返回该引用。
参考链接:
String放入运行时常量池的时机与String.intern()方法解惑
由常量池 运行时常量池 String intern方法想到的(三)之String内存模型
由常量池 运行时常量池 String intern方法想到的(四)之深入理解intern
深入解析String#intern 及其可能带来的问题