Вопрос по поводу оператора == в Java - PullRequest
4 голосов
/ 26 сентября 2010
public class Demo {  

    public static void main(String[] args) {

        String s1 = "Hello";
        String s2 = "Hello";
        System.out.println("s1 == s2 " + (s1 == s2));

        String s5 = "Hel" + "lo";
        String s6 = "He" + "llo";
        System.out.println("s5 == s6 " + (s5 == s6));

        String s7 = "He";
        String s8 = "Hello";
        s7 = s7.concat("llo");
        System.out.println("s7 == s8 " + (s7 == s8));

        String s10 = "He";
        s10 = s10 + "llo";
        System.out.println("s1 == s10 "+(s1 == s10));
    }
}

В предыдущем коде s7 == s8 и s1 == s10 дают false. Может кто-нибудь объяснить мне, что на самом деле здесь произошло в s7 = s7.concat ("llo"); и s10 = s10 + "llo"; Я понимаю, что оператор == проверяет ссылку, а equal () проверяет содержимое объекта. Но мне нужно знать, почему битовые комбинации эталонных переменных s7 и s10 отличаются от s8 и s1. Если эти вещи связаны со строками времени компиляции и строками времени исполнения, то как я могу определить, является ли это строкой времени компиляции или выполнения?

Ответы [ 7 ]

11 голосов
/ 26 сентября 2010

Причина, по которой это происходит, в том, что Java оптимизирует компилятор.Когда он видит, что вы присваиваете s1 буквальную строку "Hello", он использует то же самое «Hello» для s2, поскольку все операции Java String являются неразрушающими (например, они возвращают клон, а не модифицируют оригинал),это безопасная вещь.

То же самое относится и к "Hel" + "lo" против "He" + "llo";достаточно умно понять, что это одно и то же.

Остальные достаточно сложны, чтобы не оптимизировать их, и в результате вы получаете отдельные объекты.

4 голосов
/ 27 сентября 2010

Ответ Клинта в порядке, но я остановлюсь на нем дальше и объясню на уровне компилятора.

Как вы знаете, s1 и s2 в конечном итоге станут ссылками на один и тот же экземпляр строки, "Hello".

Для s5 и s6 компилятор видит константные выражения. То есть он видит операцию между двумя константами (строковыми литералами). Компилятор знает, как добавить строки и каков будет результат. Поскольку значения известны сразу во время компиляции, он выполняет сложение за вас, в результате чего получается буквенная строка "Hello". Следовательно, оно имеет то же значение, что и s1 и s2, поэтому каждый из них будет ссылаться на один и тот же экземпляр.

s7 нельзя упростить таким же образом. s7 изначально начинается с "He" конечно. Разница здесь в том, что s7 = s7.concat("llo"); переназначает s7 на результат вызова функции. Это не может быть упрощено как есть. Что касается компилятора Java, результаты всех вызовов функций не известны во время компиляции. Поскольку он не знает результирующего значения, его нельзя упростить, и он остается как есть. Результирующий вызов возвращает новый экземпляр строки "Hello", который не совпадает с экземпляром во время компиляции (который разделяет s8).

s10 нельзя так же упростить. s10 изначально начинается с "He", конечно. Затем переназначается s10 = s10 + "llo"; Это не может быть упрощено. Почему вы можете спросить? Ну s10 - это не окончательное выражение переменной. С технической точки зрения, компилятор не знает его значения, потому что он не является константой. Если s10 было объявлено как final String, то это может быть константа сгиба (при назначении другой переменной).

Итак, рассмотрим эту версию вашего тестового кода:

public static void main(String[] args)
{
    String s1 = "Hello";
    String s2 = "Hello";
    System.out.println("1: s1 == s2 " + (s1 == s2));    // 1

    String s3 = "Hel" + "lo";
    String s4 = "Hel".concat("lo");
    System.out.println("2: s1 == s3 " + (s1 == s3));    // 2
    System.out.println("3: s1 == s4 " + (s1 == s4));    // 3

    String he = "He";
    String s5 = he + "llo";
    String s6 = he.concat("llo");
    System.out.println("4: s1 == s5 " + (s1 == s5));    // 4
    System.out.println("5: s1 == s6 " + (s1 == s6));    // 5

    final String fhe = "He";
    String s7 = fhe + "llo";
    String s8 = fhe.concat("llo");
    System.out.println("6: s1 == s7 " + (s1 == s7));    // 6
    System.out.println("7: s1 == s8 " + (s1 == s8));    // 7
}

Можете ли вы выяснить, какие строки являются правдой?

правда, правда, ложь, ложь, ложь, правда, ложь
Вам может быть интересно, почему 3 и 7 не соответствуют действительности. Короткий ответ, Java-компилятор не был запрограммирован
, чтобы быть достаточно умным, чтобы распознавать вызов concat (), поэтому он рассматривается как обычный вызов функции.

4 голосов
/ 26 сентября 2010

== не проверяет битовые комбинации, он сравнивает адрес памяти для объектов.Только один и тот же объект имеет один и тот же адрес памяти.

0 голосов
/ 26 сентября 2010

В приведенном вами примере это то, что происходит:

String s7 = “He”;    //s7 is an object referencing a part of memory holding “He”
String s8 = “Hello”;   //s8 is an object referencing a part of memory holding “Hello”
s7 = s7.concat(“llo”); //s7.concat(“llo”) created a new object in memory that contains “Hello” and s7 now references this now object 

(s7==s8)             //checks if the equality of the object reference and this is false since they reference different memory addresses.

(s7.equals(s8))         //this will compare s7 and s8 however way the String class compares two String objects. 
0 голосов
/ 26 сентября 2010

Оператор == только проверяет, имеют ли два объекта одинаковый адрес (указатель).Только для примитивных типов, которые не являются ссылочными (например, int, char и т. Д.), Оно сравнивает значение.

Вам необходимо использовать что-то вроде s1.equals(s2) для сравнения содержимого двух строк.

0 голосов
/ 26 сентября 2010

Вы не можете делать какие-либо предположения о строковых объектах.

ВМ могла бы усердно работать, чтобы убедиться, что два строковых объекта, содержащих точно такой же массив символов, не существуют одновременно, в то время как другие ВМ допускают дублирование.

0 голосов
/ 26 сентября 2010

Оператор равенства проверяет, совпадают ли ссылки (то есть указывают на один и тот же объект), а не совпадают ли значения ссылок. Если вам нужно проверить, равна ли одна строка другой, вы должны использовать встроенный метод .equals. Это сделает сравнение значений объекта. например,

final String expectedValue = "Foo";
final String actualValue = "F" + "oo";
if (expectedValue.equals(actualValue)) {
  // This will trigger where == would not
}

Кроме того, ради безопасности, если вы сравниваете две строки и одну константу, обычно лучше вызывать равные по константе, т.е.

String myValue = getMyValue;
boolean theSame = "Some value".equals(myValue);

Вместо

String myValue = getMyValue;
boolean theSame = myValue.equals("Some Value");

Причина в том, что вторая форма рискует исключить нулевой указатель, которого можно избежать, вызвав equals () для константной строки, которая гарантированно будет там.

...