You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java8中String intern()与==的异常现象及技术疑问

Java 8下String intern()方法的异常现象解惑

先看两段核心测试代码:

// 代码1:调用intern()
public static void main(String[] args) {
    String s1 = new String("12") + new String("21");
    s1.intern();
    String s2 = "1221";
    System.out.println(s1 == s2); // 输出true
}

// 代码2:未调用intern()
public static void main(String[] args) {
    String s1 = new String("12") + new String("21");
    // s1.intern();
    String s2 = "1221";
    System.out.println(s1 == s2); // 输出false
}

两段代码仅差s1.intern()的调用,但执行结果完全不同,以下针对你的疑问逐一解答:

疑问解答

1. s1和s2实际指向的对象是什么?

  • 代码1场景:
    • s1指向的是通过StringBuilder.toString()在堆上新建的"1221"实例;
    • s2指向的是字符串常量池中的引用,而这个引用就是s1对应的堆实例的地址(因为intern()把堆中该实例的引用存入了常量池)。
  • 代码2场景:
    • s1同样指向堆上新建的"1221"实例;
    • s2指向的是字符串常量池中新创建的"1221"实例(类加载阶段并未将"1221"入池,首次通过字面量赋值触发了入池操作)。

2. 为何调用intern()(未使用返回值)会改变执行结果?

核心误区在于:字节码常量池中的"1221"并不会在类加载时自动进入字符串常量池。

Java 8中字符串常量池存放在堆中,只有字符串被主动使用时才会触发入池:

  • 字面量"12"、"21"因为直接作为new String()的参数,属于主动使用,类加载时会被存入字符串常量池;
  • 而"1221"只是字节码常量池中的符号引用,代码中未直接通过字面量引用它时,不会自动进入字符串常量池。

代码1执行流程:

  1. s1指向堆上新建的"1221"实例;
  2. 调用s1.intern()时,字符串常量池中无"1221",因此将s1对应的堆实例的直接引用存入常量池;
  3. 后续s2 = "1221"时,发现常量池中已有该字符串的引用,直接返回这个引用(即s1的地址),因此s1 == s2为true。

代码2执行流程:

  1. s1指向堆上新建的"1221"实例;
  2. 未调用intern(),字符串常量池中无"1221";
  3. 执行s2 = "1221"时,JVM会在字符串常量池中新建一个"1221"实例,s2指向这个常量池实例,和s1的堆实例地址不同,因此s1 == s2为false。

对假设的验证

  1. 字符串常量池的入池时机:
    字节码常量池中的符号引用不会自动全部进入字符串常量池,只有当字符串被主动触发入池操作时才会进入(比如直接使用字面量赋值、调用intern()方法等)。JVM规范明确,字符串字面量的入池是在首次使用时完成,而非类加载阶段。
  2. intern()的行为:
    Java 8中,当调用intern()且常量池中无对应字符串时,会将堆中该字符串对象的直接引用存入常量池,而非复制对象。因此s2 = "1221"直接返回这个引用,和s1指向同一个对象,不存在"引用的引用"——常量池里存的就是对象的直接地址,和s1的引用值完全相同。

内容的提问来源于stack exchange,提问作者DracoYu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 11:37:35