2017-01-19 5 views
2

Я использовал JShell немного, чтобы проверить это, и сегодня я столкнулся с довольно интересным поведением.Что происходит с равенством в JShell/Java 9?

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

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

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

jshell> "A" == "A" 
$5 ==> true 

Я был первым интересно, если это была особенность Java 9, и я проверил это путем компиляции и запуска этой программы с Java 9

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 
true 
"A" == "A" 
true 

Как мой выход также. Что тут происходит? Почему a и b равны друг другу и почему "A" == "A" true?

+3

Это называется _interning_, и это то, что компилятор делает, чтобы оптимизировать себя. Вместо создания дублирующего объекта String для нового _A_ он использует тот, который у него уже есть. [Соответствующий] (http://stackoverflow.com/questions/10578984/what-is-string-interning). –

+0

Есть ли какой-нибудь переключатель, чтобы запретить Java интернировать строки без явного вызова 'String # intern'? –

+1

Вам нужно использовать '.equals()', чтобы проверить, равны или нет _Objects_, вы просто проверяете ссылку. –

ответ

10

Почему бы и нет? Это поведение проявляется и в предыдущих версиях Java - String литералы интернированы.

Как известно, == проверяет ссылочное равенство - две переменные имеют одинаковый адрес памяти. Когда String интернирован, все ссылки этой строки указывают на внутренний пул и, таким образом, будут равны с использованием ==.

+0

Это также объясняет, почему, если я делаю 'new String (« A »)' вместо '' A '', это не работает. –

+1

@Eli Sadoff: обратите внимание, что объекты, созданные с помощью 'new String (" A ")' и '' A "', будут по-прежнему ссылаться на один и тот же массив внутри ... – Holger

+0

@Holger Они оба '{'A', '\ 0 '} ', нет? –

1

Я просто хотел добавить эту демонстрацию рядом с точным ответом Sinkingpoint.
Это не безопасно использовать == на струнных, если вы не знаете, происхождение каждого из них, так как строка, которая встраивается в некотором роде (например, new String("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)); 
    } 
} 

Выход из этого (бег на https://repl.it/languages/java) является:

A == A -> true 
oneA == twoA -> true 
oneA == three -> false 
oneA.intern() == three.intern() -> true 
oneA .equals three -> true 

Вы можете безопасно использовать string1.equals(string2) или string1.intern() == string2.intern()