String常量池
配套学习资料【含授课视频、源码】:https://link3.cc/ashao
常见面试题:
分析下面代码的输出结果。
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // ?
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s3 == s4); // ?
System.out.println(s1 == s3); // ?
}
//程序输出
true
false
false
面试题分析:
创建字符串对象,和其他对象一样,会占用计算机的资源(时间、空间)。大量且频繁的创建字符串对象,会极大地影响程序的性能。
JVM为了提高性能、减少内存开销,开辟了一个字符串常量池(类似缓存区)。
String常量池:
在Java中,String常量池是一块特殊的内存区域,用于存储字符串常量。String常量池的设计目的是为了节省内存和提高性能。
当我们创建字符串常量时,如果字符串常量池中已经存在相同内容的字符串,那么新创建的字符串常量会直接引用已存在的字符串对象,而不会创建新的对象。这样可以避免重复创建相同内容的字符串,节省内存空间。
在JDK8及之后的版本中,字符串常量池的位置与其他对象的存储位置,都位于堆内存中。这样做的好处是,字符串常量池的大小可以根据需要进行调整,并且可以享受到垃圾回收器对堆内存的优化。
Java将字符串放入String常量池的方式:
-
直接赋值:通过直接赋值方式创建的字符串常量会被放入常量池中。
例如:
String str = "Hello";
-
调用String类提供intern()方法:可以将字符串对象放入常量池中,并返回常量池中的引用。
例如:
String str = new String("World").intern();
注意:通过new关键字创建的字符串对象不会放入常量池中,而是在堆内存中创建一个新的对象。只有通过直接赋值或调用intern()
方法才能将字符串放入常量池中。
案例1:
package com.briup.chap07.test;
public class Test062_String {
public static void main(String[] args) {
String s1 = "Hello"; // 字符串常量,放入常量池
String s2 = "Hello"; // 直接引用常量池中的字符串对象
System.out.println(s1 == s2); // true,引用相同
// 直接new String对象,不会将'World'放入常量池
String s3 = new String("World");
// 调用intern()方法,将'World'放入常量池,并返回常量池中的引用
String s4 = new java.lang.String("World").intern();
String s5 = "World";
System.out.println(s3 == s4); // false,引用不同
System.out.println(s4 == s5); // true,引用相同
}
}
对应内存图:
案例2:
package com.briup.chap07.test;
public class Test062_String2 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
// 常量优化机制:"a" 和 "b"都是字面值常量,借助 + 连接,其结果 "ab" 也被当作常量
String s3 = "a" + "b";
String s4 = "ab";
System.out.println(s3.equals(s4)); // true
System.out.println(s3 == s4); // true
System.out.println("-------------");
String s5 = s1 + s2;
System.out.println(s4.equals(s5)); // true
System.out.println(s4 == s5); // false
System.out.println("-------------");
String s6 = (s1 + s2).intern();
System.out.println(s4.equals(s6)); // true
System.out.println(s4 == s6); // true
}
}
注意事项:
使用 + 拼接多个字符串常量,拼接的结果仍旧是字符串常量
如果结果字符串常量在常量池中不存在,则Java会将其放入到字符串常量池中
//final修饰的也是常量
public static void main(String[] args) {
String str = "ab";
String s1 = "a";
// String msg = new String("a"+"b");
String msg = s1 + "b";
System.out.println(msg == str); //false
//如果final修饰的变量参与运算,等同于字面值常量
final String s2 = "a";
// String msg2 = "a" + "b";
String msg2 = s2 + "b";
System.out.println(msg2 == str); //true
}