一、JVM 常量池中存储的是对象还是引用呢
答:String类型存放的是对象的引用。
当字符串不存在的时候,会在堆中创建字符串对象,然后再在字符串常量池当中创建这个引用(常量池指向堆中的字符串)。
参考链接:JVM 常量池中存储的是对象还是引用呢?
摘录:如果您说的确实是runtime constant pool(而不是interned string pool / StringTable之类的其他东西)的话,其中的引用类型常量(例如CONSTANT_String、CONSTANT_Class、CONSTANT_MethodHandle、CONSTANT_MethodType之类)都存的是引用,实际的对象还是存在Java heap上的。
二、字符串拼接
String 类型的变量和常量做“+”运算时发生了什么?
注意:用 ==
比较的时候,比较的是地址值是否相等,可以理解为判断是否指向了同一个对象。
三、String#intern 方法有什么作用
String.intern() 是一个 native(本地)方法,其作用是将指定的字符串对象的引用保存在字符串常量池中,可以简单分为两种情况:
- 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
- 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。(不会再新建一个字符串对象了)
四、创建对象个数
1.String str = “abc”;这句话创建了几个字符串对象?
答:0个或1个对象。
创建字符串“abc"时会先在字符串池中查找,看是否有相等的对象引用,没有的话就在堆中创建“abc”字符串对象,同时把地址驻留在字符串池,这个时候会创建一个对象;有的话则直接赋值为常量池中的引用,避免重复创建字符串对象,这个时候会创建0个对象。
2. String str = “ab”+“c”;这句话创建了几个字符串对象?
答:同上,0个或1个对象,JVM会将字符串常量的拼接优化成最终结果”abc“
3. String str = new String(“abc”);这句话创建了几个字符串对象?
答:创建一个或者两个对象。
(1)我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待;
(2)String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;
(3)=是对变量str进行初始化,将某个对象的引用(或者叫地址)赋值给它,显然也没有创建对象;
(4)new String(“abc”)可以被看成"abc"和new String()
- 创建字符串“abc"时会先在字符串池中查找,看是否有相等的对象引用,没有的话就在堆中创建“abc”字符串对象,同时把地址驻留在字符串池,相当于新建一个字符串对象;有的话则直接用常量池中的引用,避免重复创建字符串对象,相当于创建0个字符串对象。
- 通过构造函数new String(“abc”)在堆内存中创建的新字符串对象。
- 注:第一步为什么要新增一个“abc”呢,是因为享元模式,保证后续的重复利用。
4. String str = new String(“ab”) + “c”;这句话创建了几个字符串对象?
答:最少两个(new String()新建的、最后拼接生成的),最多四个。
注:为什么new StringBuilder().toString()
内层的new String(value, 0, count);
方法不会再多新建一个对象?因为此时并不是双引号的字符串,不是字符串字面量。所以不会再新建一个对象,不会被主动推送到常量池中。
为什么new String(“abc”)
会新建abc对象?因为new String()
里面是双引号,所以会新建对象。
五、有关intern()用法的地址是否相等
1. jdk7之后的环境中,以下代码,输出结果是什么
String str5 = new String("1")+new String("1");
String str7 = str5.intern();
String str6 = "11";
System.out.println(str5 == str6); //true
System.out.println(str7 == str6); //true
(1)创建字符串“1"时会先在字符串池中查找,看是否有相等的对象引用,没有的话就在堆中创建“1”字符串对象,同时把地址驻留在字符串池。
(2)new String(“1”)创建了两个对象。
(3)new String(“1”)+new String(“1”);会调用StringBuilder的append()方法,将字符串“11”存放在堆中。
(4)str5.intern();会查找常量池中是否有“11”字符串的引用,如果有的话则直接返回该引用,没有的话常量池中创建一个指向该字符串对象的引用并返回。假设目前是没有的,所以会在字符串常量池中创建一个引用指向堆中已存在的(也就是该字符串对象str5)字符串“11”,并将这个引用赋值给str7,注意此时str5的指向是不变的。
(5)String str6 = “11”;因为现在字符串“11”已经存在在常量池中,所以str6会赋值为常量池中的引用。
(6)因为str5,str6,str7都赋值为字符串常量池中的引用(指向堆中的“11”),所以结果输出为true。
2. jdk7之后的环境中,以下代码,输出结果是什么
String str5 = new String("1")+new String("1");
String str6 = "11";
String str7 = str5.intern();
System.out.println(str5 == str6); //false
System.out.println(str7 == str6); //true
和4.1的内容相比只是intern()的赋值顺序发生了改变。
(1)创建字符串“1"时会先在字符串池中查找,看是否有相等的对象引用,没有的话就在堆中创建“1”字符串对象,同时把地址驻留在字符串池。
(2)new String(“1”)创建了两个对象。
(3)new String(“1”)+new String(“1”);会调用StringBuilder的append()方法,将字符串“11”存放在堆中。
(4)String str6 = “11”;假设此时字符串“11”不在常量池中,所以会在堆中新建一个字符串对象“11"并且在常量池中存放该引用。
(5)String str7 = str5.intern();字符串常量池中保存了对应的字符串对象的引用,就直接返回字符串11的引用。此时str5的指向是不变的,还是指向堆中的对象。
(6)所以,str5和str6的地址值不同,str7和str6的地址值相同。