目录
一、堆与字符串常量池
二、Java创建字符串对象的两种方式
1. 字面值方式创建字符串对象
2. 使用new关键字创建字符串对象
三、经典面试问题之创建了几个对象?
1. String s1 = "11";
2. String s1 = "1" + "1";
3. String s1 = new String("11");
4. String s1 = new String("1") + new String("1");
四、String.intern()的作用
1. 如果常量池中已经存在该值
2. 如果常量池中不存在该值
一、堆与字符串常量池
堆是Java虚拟机中存储对象实例和数组的内存区域。 字符串常量池(String Pool)是Java虚拟机在内存中专门为字符串字面量分配的一块区域。它用于存储所有用双引号声明的字符串常量,以及运行期间调用intern()方法生成的字符串。常量池中的相同内容的字符串只会保存一份,多个引用会指向同一个对象,从而节省内存并提高效率。、
字符串池的实现有一个前提条件:String对象是不可变的。因为这样可以保证多个引用可以同时指向字符串池中的同一个对象。如果字符串是可变的,那么一个引用操作改变了对象的值,对其他引用会有影响,这样显然是不合理的。
二、Java创建字符串对象的两种方式
1. 字面值方式创建字符串对象
public static void main(String[] args) {
String str1 = "aaa";
String str2 = "aaa";
System.out.println(str1 == str2); // 输出 true
}
当使用字面值(如"aaa")创建字符串时,JVM会先在字符串常量池中查找是否存在该字符串对象。如果不存在,则在常量池中创建,并返回其引用。如果已存在,则直接返回常量池中该对象的引用。因此,str1和str2都指向常量池中的同一个"aaa"对象,str1 == str2结果为true。
2. 使用new关键字创建字符串对象
public static void main(String[] args) {
String str1 = new String("aaa");
String str2 = new String("aaa");
System.out.println(str1 == str2); // 输出 false
}
使用new String("aaa")时,JVM会先检查常量池中是否有"aaa"对象。如果没有,先在常量池中创建"aaa"对象。然后,无论常量池中是否有"aaa",都会在堆内存中新建一个"aaa"对象,并返回堆对象的引用。每次new String("aaa")都会在堆中创建新的对象,因此str1和str2指向不同的堆对象,str1 == str2结果为false。
三、经典面试问题之创建了几个对象?
1. String s1 = "11";
"11"是字符串字面量,存储在字符串常量池中。s1指向常量池中的"11"对象。创建对象数量:1(常量池1个)。
2. String s1 = "1" + "1";
"1" + "1"在编译期会被优化为"11",等价于String s1 = "11"。创建对象数量:1(常量池1个)。
3. String s1 = new String("11");
new String("11")会在堆上创建一个新的String对象,内容为"11"。常量池中也有"11"(因为字面量被用来创建堆对象)。s1指向堆上的"11"对象。创建对象数量:2(常量池1个,堆1个)。
4. String s1 = new String("1") + new String("1");
new String("1")会在堆上创建两个新的String对象,各自内容为"1",但常量池中也有"1"。+操作会在堆上创建一个新的String对象,内容为"11"。结果:s1指向堆上的"11",常量池中也有"1",但没有"11"。创建对象数量:4(常量池1个,堆3个)。
四、String.intern()的作用
intern() 的作用是确保常量池中有该字符串的唯一引用,并返回该引用:
1. 如果常量池中已经存在该值
intern() 会直接返回常量池中已有的字符串引用,不会加入当前对象。
String a = "abc";
String b = new String("abc");
System.out.println(b.intern() == a); // true
这里常量池中已有 "abc",b.intern() 返回的就是 a 指向的常量池对象。
2. 如果常量池中不存在该值
intern() 会把当前字符串对象的引用放入常量池,并返回该引用。
String s1 = new String("x") + new String("y");
System.out.println(s1.intern() == s1); // true
因为 "xy" 不在常量池中,intern() 把 s1 的引用放入池中,所以返回 s1 本身。