【问题标题】:String#intern() in jdk8jdk8中的字符串#intern()
【发布时间】:2023-04-10 08:51:01
【问题描述】:

为什么这两个相似的代码sn-ps会导致不同的结果?

public class Test {
    @org.junit.Test
    public void test1() {
        String s3 = "1" + new String("1");
        String s5 = s3.intern();
        System.out.println(s5 == s3);
    }

    @org.junit.Test
    public void test2() {
        String s3 = "g" + new String("g");
        String s5 = s3.intern();
        System.out.println(s5 == s3);
    }
}   

在 jdk 1.8.0_144 环境下试过,得到了这些答案(在 jdk6 中可能不同):

假的 和 真的

第一个 sn-p 在 Java8 中应该为真。 但更令人困惑的是,如果我将代码移到 main 方法中,它会导致 true。

public class TestWithoutJunit {
    public static void main(String[] args) {
        String s3 = "1" + new String("1");
        String s5 = s3.intern();
        System.out.println(s5 == s3);
    }
}

我假设这是一种 JUnit 优化,所以我检查了字节码,jUnit 中的两个代码 sn-p 和 main 函数保持完全相同。

顺便说一下,我的 Java 版本

java 版本“1.8.0_144” Java(TM) SE 运行时环境 (build 1.8.0_144-b01) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.144-b01,混合模式)

【问题讨论】:

  • @AndréStannek 如果您理解这个问题,则不会。
  • 无法复制。 java version "1.8.0_172" Java(TM) SE Runtime Environment (build 1.8.0_172-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode).
  • 在 jdk10 上运行这些 sn-ps,得到所有 trues(应该是这样)
  • @Stylex 可能是因为 JUnit 导致 sun.text.resources.FormatData 类在您的代码之前加载。

标签: java string junit


【解决方案1】:

大概gg 发生在String Pool 虽然我不知道是怎么来的,也许 Junit 在后台做了一些先前的事情。 如果字符串字面量在字符串池中,它再次引用以代替再次实例化。这就是为什么您在代码的第二部分中使用== 运算符获得true 的原因。 您应该始终使用equals 方法进行某些字符串比较。

你明白intern()方法实际上在做什么吗?

可以使用 String 的 intern() 方法将字符串对象添加到此池中。对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为真时,s.intern() == t.intern()true

所有文字字符串和字符串值的常量表达式都被保留。

@Edit: OP 作为评论说,

我检查了 Constant Pool,在此之前找不到像 "11" 这样的东西 线路:String s4 = "11";

您无法从 Java 代码访问 字符串池,至少在 Java VM 的 HotSpot 实现中不能。所以这就是为什么你在String s4 = "11"之前看不到11

【讨论】:

  • 如果我只运行这段代码: String s3 = "1" + new String("1");字符串 s5 = s3.intern(); System.out.println(s5 == s3); JUnit 中的结果将是错误的。如果我将此代码 sn-p 从 JUnit 移动到 public static void main(String[] args),结果将为真。我检查了两个版本的字节码,应该是一样的。我检查了常量池,在这一行之前找不到像“11”这样的东西:String s4 =“11”;
  • @Stylex 也进行了编辑,也许 Junit 在幕后做了一些之前的事情。
  • OP 可能检查了类的常量池,而不是字符串池
  • @GotoFinal 是的,我写它是为了突出他正在寻找的地方。
【解决方案2】:

就像其他人说的那样,你应该使用 .equals 来比较字符串,但是如果你想知道为什么在这种情况下你会得到不同的结果,那么:

默认情况下,每个字符串文字都添加到字符串池中,因为"1"字符串已经在不同的类中用作文字,所以.intern返回不同的实例,对于"g",这种文字没有其他用法,所以你的是第一个添加到字符串池的 - 所以.intern 在这种情况下不会改变任何东西 - 因为它返回与你相同的实例。
"11""gg" 字符串相同。
确切地说,在 JDK 8 上,sun.text.resources.FormatData 似乎使用了它。 (两个字符串,"1""11"

如果您要添加其他带有 "g" 文字的类,该文字将在此之前加载 - 您将获得与 "1" 相同的结果。

【讨论】:

  • 如果我只运行这段代码: String s3 = "1" + new String("1");字符串 s5 = s3.intern(); System.out.println(s5 == s3); JUnit 中的结果将是错误的。如果我将此代码 sn-p 从 JUnit 移动到 public static void main(String[] args),结果将为真。我检查了两个版本的字节码,应该是一样的。我检查了常量池,在这一行之前找不到像“11”这样的东西:String s4 =“11”;
  • @Stylex 可能是因为 JUnit 导致 sun.text.resources.FormatData 类加载。
猜你喜欢
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 2020-12-21
  • 2020-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多