代码片段 1
String s1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(s1.intern() == s1);
你的输出结果是?
代码片段 2
String s1 = new String("计算机软件");
System.out.println(s1.intern() == s1);
你的输出结果是?
首先这两个代码片段的执行结果是不一致,另外不一致的原因,涉及到字符串常量池的管理和**intern() 方法的行为**。下面逐步分析其原因:
代码片段 1
String s1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(s1.intern() == s1);
-
new StringBuilder("计算机").append("软件").toString()- 创建了一个
StringBuilder对象,并通过append()方法拼接生成了一个新的字符串对象。 - 此时,
s1是一个在堆中创建的新String对象,内容为"计算机软件"。
- 创建了一个
-
调用
intern()方法intern()会尝试将字符串加入字符串常量池。- 如果常量池中已经存在内容为
"计算机软件"的字符串,intern()返回池中的引用。 - 如果常量池中没有内容为
"计算机软件"的字符串,则将堆中的s1引用添加到常量池,并返回s1本身。
-
特殊行为:JVM 对常量池管理
- 在 JVM 的实现中,某些字符串(尤其是字面量字符串)在类加载阶段会提前加载到字符串常量池。
- 对于
"计算机软件",如果它之前没有出现在代码中作为字面量,则s1.intern()将会把s1放入常量池,并返回s1的引用。
-
结果
- 因为
"计算机软件"之前不在常量池中,s1.intern()返回的引用就是s1。 - 所以输出为:
true。
- 因为
代码片段 2
String s1 = new String("计算机软件");
System.out.println(s1.intern() == s1);
-
new String("计算机软件")- 创建了一个新的
String对象s1,它存储在堆中。 - 此时,常量池中已经存在
"计算机软件",因为字面量"计算机软件"是类加载过程中会被放入常量池的。
- 创建了一个新的
-
调用
intern()方法intern()会返回常量池中已经存在的"计算机软件"的引用。- 因为
"计算机软件"是字面量,常量池中早已存在。 - 所以
s1.intern()返回的是常量池中的引用,而s1是堆中的对象。
-
结果
s1.intern()和s1是两个不同的引用。- 所以输出为:
false。
总结两段代码的区别
| 特性 | 代码片段 1 | 代码片段 2 |
|---|---|---|
| 字符串常量池是否已有字符串 | "计算机软件" 不在常量池中,s1 被放入池中。 | "计算机软件" 已在常量池中,因为是字面量。 |
intern() 的返回值 | 返回堆中的 s1 引用。 | 返回常量池中的字符串引用。 |
| 比较结果 | true | false |
注意事项
- 字符串常量池的行为依赖于 JVM 的具体实现,尤其是在不同的 Java 版本(如 JDK 6、7、8)中可能会有些许差异。
- 片段 1 的结果依赖于
"计算机软件"未作为字面量提前加载到常量池。