JShell/Java9中的平等性是怎么回事?

JShell/Java9中的平等性是怎么回事?,java,java-9,jshell,Java,Java 9,Jshell,我一直在使用JShell来测试它,今天我遇到了一些非常有趣的行为 jshell> String a = "A" a ==> "A" jshell> String b = "A" b ==> "A" jshell> a == b $4 ==> true jshell> "A" == "A" $5 ==> true 我首先想知道这是否是Java9的一个特性,然后我用Java9编译并运行了这个程序 public class Equus {

我一直在使用JShell来测试它,今天我遇到了一些非常有趣的行为

jshell> String a = "A"
a ==> "A"

jshell> String b = "A"
b ==> "A"

jshell> a == b
$4 ==> true

jshell> "A" == "A"
$5 ==> true
我首先想知道这是否是Java9的一个特性,然后我用Java9编译并运行了这个程序

public class Equus {
    public static void main(String... args) {
        String a = "A";
        String b = "A";
        System.out.println("a == b");
        System.out.println(a == b);
        System.out.println("\"A\" == \"A\"");
        System.out.println("A" == "A");
    }
}
有趣的是,我得到了


以及我的输出。这是怎么回事?为什么
a
b
彼此相等,为什么
“a”==“a”
为真

为什么不应该呢?这种行为在以前的Java版本中也有体现——字符串文本被插入


正如您所知,
==
检查引用相等性-这两个变量具有相同的内存地址。当一个字符串被插入时,该字符串的所有引用都指向插入池,因此使用
==
将相等

我只是想在Sinkingpoint的好答案旁边添加这个演示。
除非您知道每个字符串的来源,否则在字符串上使用
=
是不安全的,因为以某种方式构建的字符串(例如Eli注释中的
新字符串(“a”)
,或此处使用的
.toString()
)不是相同的引用,即使两者使用相同的底层字符数组

class Main
{
  public static void main(String[] args)
  {
    String oneA = "A";
    String twoA = "A";
    String three = new StringBuilder().append('A').toString();

    // We expect constant literals to be ==
    System.out.print("A == A -> ");
    System.out.println("A" == "A");

    // Variables assigned the same literal are also ==
    System.out.print("oneA == twoA -> ");
    System.out.println(oneA == twoA);

    // but a built-up String var is not == to the "literal" var.
    System.out.print("oneA == three -> ");
    System.out.println(oneA == three);

    // If we intern() them they are again ==
    System.out.print("oneA.intern() == three.intern() -> ");
    System.out.println(oneA.intern() == three.intern());

    // and of course, .equals() is always safe.
    System.out.print("oneA .equals three -> ");
    System.out.println(oneA.equals(three));
  }
}
此(运行)的输出为:


您可以安全地使用
string1.equals(string2)
string1.intern()==string2.intern()

这称为interning,是编译器为优化自身而做的事情。它不为新的a创建重复的字符串对象,而是使用它已有的字符串对象。您需要使用
.equals()
来检查对象是否相等,您只是检查引用。这是Java自第一个版本以来的行为。为什么
“A”==“A”
应该是
false
?这与java-9无关,这可能是重复的,进一步解释了为什么如果我使用
新字符串(“A”)
而不是
“A”
,这不起作用。@Eli Sadoff:注意通过
新字符串(“A”)
“A”创建的对象
仍将在内部引用同一数组…@Holger它们都是
{'A','\0'}
,否?@Eli Sadoff:否,Java字符串从不以零结尾。但我不是指数组的内容。我想强调的是,它们指向同一个数组实例。因此在
String
类中,
newstring(“A”).value==“A”。value
将是
true
@Holger-Oh。这更有道理。
class Main
{
  public static void main(String[] args)
  {
    String oneA = "A";
    String twoA = "A";
    String three = new StringBuilder().append('A').toString();

    // We expect constant literals to be ==
    System.out.print("A == A -> ");
    System.out.println("A" == "A");

    // Variables assigned the same literal are also ==
    System.out.print("oneA == twoA -> ");
    System.out.println(oneA == twoA);

    // but a built-up String var is not == to the "literal" var.
    System.out.print("oneA == three -> ");
    System.out.println(oneA == three);

    // If we intern() them they are again ==
    System.out.print("oneA.intern() == three.intern() -> ");
    System.out.println(oneA.intern() == three.intern());

    // and of course, .equals() is always safe.
    System.out.print("oneA .equals three -> ");
    System.out.println(oneA.equals(three));
  }
}
A == A -> true
oneA == twoA -> true
oneA == three -> false
oneA.intern() == three.intern() -> true
oneA .equals three -> true