字符串常量池,以下String字符串比较,来看看你能不能做对

74 阅读3分钟

代码片段 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);
  1. new StringBuilder("计算机").append("软件").toString()

    • 创建了一个 StringBuilder 对象,并通过 append() 方法拼接生成了一个新的字符串对象。
    • 此时,s1 是一个在堆中创建的新 String 对象,内容为 "计算机软件"
  2. 调用 intern() 方法

    • intern() 会尝试将字符串加入字符串常量池。
    • 如果常量池中已经存在内容为 "计算机软件" 的字符串,intern() 返回池中的引用。
    • 如果常量池中没有内容为 "计算机软件" 的字符串,则将堆中的 s1 引用添加到常量池,并返回 s1 本身。
  3. 特殊行为:JVM 对常量池管理

    • JVM 的实现中,某些字符串(尤其是字面量字符串)在类加载阶段会提前加载到字符串常量池。
    • 对于 "计算机软件",如果它之前没有出现在代码中作为字面量,则 s1.intern() 将会把 s1 放入常量池,并返回 s1 的引用。
  4. 结果

    • 因为 "计算机软件" 之前不在常量池中,s1.intern() 返回的引用就是 s1
    • 所以输出为:true

代码片段 2

String s1 = new String("计算机软件");
System.out.println(s1.intern() == s1);
  1. new String("计算机软件")

    • 创建了一个新的 String 对象 s1,它存储在堆中。
    • 此时,常量池中已经存在 "计算机软件",因为字面量 "计算机软件" 是类加载过程中会被放入常量池的。
  2. 调用 intern() 方法

    • intern() 会返回常量池中已经存在的 "计算机软件" 的引用。
    • 因为 "计算机软件" 是字面量,常量池中早已存在。
    • 所以 s1.intern() 返回的是常量池中的引用,而 s1 是堆中的对象。
  3. 结果

    • s1.intern()s1 是两个不同的引用。
    • 所以输出为:false

总结两段代码的区别

特性代码片段 1代码片段 2
字符串常量池是否已有字符串"计算机软件" 不在常量池中,s1 被放入池中。"计算机软件" 已在常量池中,因为是字面量。
intern() 的返回值返回堆中的 s1 引用。返回常量池中的字符串引用。
比较结果truefalse

注意事项

  • 字符串常量池的行为依赖于 JVM 的具体实现,尤其是在不同的 Java 版本(如 JDK 6、7、8)中可能会有些许差异。
  • 片段 1 的结果依赖于 "计算机软件" 未作为字面量提前加载到常量池。