1、引入问题
String s = new String("abc"); //创建了几个对象
没错我也是从这开始的,下面先说答案,再进行验证
String s = new String("abc"); //1、堆上创建"abc"对象,2、常量池如果有"abc"对象则不在创建,否则在常量池创建一个"abc"对象。3、栈上创建一个引用s指向堆上的对象"abc"
String str = "abc"; //从常量池取,如果有则返回,没有则在常量池创建一个"abc"对象
2、字符串常量池
这方面没找到可靠的官网资料,只能从网上查一些大佬的博客,加上代码验证,博客附在最后
1)jdk1.6下,字符串常量池位于堆的 Perm 区(不同于堆的正常区域,Perm 区和正常的 JAVA Heap 区域是完全分开),Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError: PermGen space
错误的。
字符串常量池的存储是一个长度1009的哈希表,key是字符串常量,如上述代码是“abc”,value是字符串常量池中“abc”对象的地址(或者叫引用)。也就是说字符串常量池中有字符串对象,同时也有对应的哈希表,用于快速检索字符串对象。
2)jdk1.7下,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称 jdk8 已经直接取消了 Perm 区域,而新建立了一个元区域。应该是 jdk 开发者认为 Perm 区域已经不适合现在 JAVA 的发展了。
对字符串常量池的影响是,如果堆上有“abc”对象,则字符串常量池里不需要再多创建一个“abc”对象了,可以直接引用堆上的对象。也就是说哈希表上,key是“abc”的地方,value可以是堆上的“abc”对象的地址,可以是但不一定是。
3、验证
String
提供的intern
方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串对象放入常量池中。注意这是创建了新对象,一个在堆上,一个在字符串常量池中,用==比较地址时是不一样的。
jdk1.8下
String str = new String("11"); //注意无论是1.6还是1.7,如果字符串常量池中没有“11”对象,new String("11")这种方式只要字符串常量池中没有"11"对象,都会创建两个对象,一个在堆上,一个在字符串常量池中,所以这里创建两个"11"对象,和栈上的str引用,引用的是堆对象 str.intern(); //这里感觉没有什么意义 String str1 = "11"; //从字符串常量池中取 至于字符串常量池上的哈希表是什么时候填充的key为"11",value为字符串常量池中"11"对象的地址的这条记录,我不清楚,我猜测是在上面new String对象的时候 System.out.println(str==str1);//false 由于str引用的是堆上的对象 str1是字符串常量池中的对象,所以地址肯定不同。虽然字符串常量池在堆上,但还是有区别的,部分与整体的区别,在堆上不一定在字符串常量池
上面代码验证了堆上和字符串常量池中都存在"abc"对象
String s = new String("1")+new String("1"); //第一个new String("1")创建了两个"1"对象,一个在堆上,一个在字符串常量池。第二个new String("1")只创建一个"1"对象,在堆上。
这里的s引用的对象"11" 是在堆上?还是在字符串常量池中?我认为是在堆上,同时字符串常量池中没有"11"对象,下面进行验证:
//1、认为在字符串常量池中,哈希表上引用的是字符串常量池中的对象 String s1 = "11"; //从常量池的哈希表取 System.out.println(s==s1);//结果为false,假设失败,常量池中没有"11"对象
//2、认为在堆上,并且常量池中也没有,哈希表引用的是堆上的"11"对象,验证的时候新建一个方法,不然上面代码已经在常量池中创建"11"对象了,代码如下
String s = new String("1")+new String("1");
s.intern(); //字符串常量池没有"11"对象,则把s引用赋给 字符串常量池里哈希表key为"11"的那条记录的value,也就是说字符串常量池里没有"11"对象,value指向的是堆
String s1 = "11"; //从常量池的哈希表取堆上对象的地址
System.out.println(s==s1);//结果为true
//3、执行完第二段代码已经知道堆上有"11"对象,而常量池中没有,如果我们再new String("11")对象,会怎样?因为new对象,会在堆上新建一个,同时由于字符串常量池中没有"11"对象,所以也会在常量池新建一个,那么哈希表"11"的key对应得value将指向堆还是池?
引用的博客:
https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html