在 Java 中,String str = "abc"
和 String str = new String("abc")
两种写法看似相似,实则在内存存储和对象创建上有本质区别,核心差异在于是否会触发常量池的使用。
1. 字符串常量池(String Pool):理解的关键
Java 为了优化字符串操作,在内存中开辟了一块特殊区域 ——字符串常量池(位于方法区),用于存储唯一的字符串字面量。
- 当出现相同的字符串字面量时,常量池中只会保存一份,避免重复创建,节省内存。
2. 两种写法的区别
(1)String str = "abc"
(字面量赋值)
- 创建过程:
JVM 会先检查字符串常量池中是否存在 "abc":- 如果存在,直接让
str
引用常量池中的这个字符串对象; - 如果不存在,先在常量池中创建 "abc" 对象,再让
str
引用它。
- 如果存在,直接让
- 内存情况:只在常量池中可能创建一个对象(若之前没有),不会在堆中创建新对象。
String s1 = "abc";
String s2 = "abc";
// s1 和 s2 引用的是常量池中同一个对象,所以地址相同
System.out.println(s1 == s2); // 输出 true(== 比较的是对象地址)
(2)String str = new String("abc")
(new 关键字创建)
- 创建过程:
无论常量池中是否存在 "abc",都会在堆内存中创建一个新的字符串对象,并且这个对象的值会引用常量池中的 "abc"(如果常量池中没有,会先在常量池创建 "abc")。 - 内存情况:至少在堆中创建一个新对象,可能在常量池创建一个对象(若之前没有)。
String s3 = new String("abc");
String s4 = new String("abc");
// s3 和 s4 是堆中两个不同的对象,地址不同
System.out.println(s3 == s4); // 输出 false
// 堆中对象的值引用常量池,所以内容相同
System.out.println(s3.equals(s4)); // 输出 true(equals 比较字符串内容)
3. 本质区别总结
写法 | 对象创建位置 | 常量池使用情况 | 内存效率 |
---|---|---|---|
String s = "abc" | 仅常量池(最多 1 个) | 优先复用常量池已有对象 | 高(复用机制) |
String s = new String("abc") | 堆内存(至少 1 个) | 可能触发常量池创建(若不存在) | 低(新对象) |
4. 什么时候用哪种方式?
- 优先用字面量赋值(
"abc"
):大多数场景下,不需要在堆中创建新对象,利用常量池的复用机制,节省内存,效率更高。 - 必要时用
new String()
:仅当需要在堆中创建一个全新的字符串对象(与常量池中的对象完全独立)时使用(实际开发中极少用到,除非特殊需求)。
一句话概括
"abc"
是 “复用优先”,尽量用常量池中的同一个对象;new String("abc")
是 “强制新建”,一定会在堆中产生新对象,两者的内存地址不同。