== оператор со строками - PullRequest
23 голосов
/ 05 июля 2011

Приведенный ниже код не должен печатать «Пока», так как оператор == используется для сравнения ссылок, но, как ни странно, «Пока» все еще печатается. Почему это происходит? Я использую Netbeans 6.9.1 в качестве IDE.

public class Test {
    public static void main(String [] args) {
        String test ="Hi";
        if(test=="Hi"){
            System.out.println("Bye");
        }
    }
}

Ответы [ 4 ]

42 голосов
/ 05 июля 2011

Это происходит из-за интернирования . Поведение описано в документации для String#intern (включая причины его появления в вашем коде, даже если вы никогда не вызываете String#intern):

Пул строк, изначально пустой, поддерживается в частном порядке классом String.

Когда вызывается метод intern, если пул уже содержит строку, равную этому объекту String, как определено методом equals(Object), возвращается строка из пула. В противном случае этот объект String добавляется в пул и возвращается ссылка на этот объект String.

Отсюда следует, что для любых двух строк s и t, s.intern() == t.intern() равен true тогда и только тогда, когда s.equals(t) имеет значение true.

Все литеральные строки и строковые константные выражения интернированы. Строковые литералы определены в §3.10.5 Спецификации языка Java .

Так, например:

public class Test {

    private String s1 = "Hi";

    public static void main(String [] args) {

        new Test().test();
        System.exit(0);
    }

    public void test() {
        String s2 ="Hi";
        String s3;

        System.out.println("[statics]          s2 == s1? " + (s2 == s1));
        s3 = "H" + part2();
        System.out.println("[before interning] s3 == s1? " + (s3 == s1));
        s3 = s3.intern();
        System.out.println("[after interning]  s3 == s1? " + (s3 == s1));
        System.exit(0);
    }

    protected String part2() {
        return "i";
    }
}

Выход:

[statics]          s2 == s1? true
[before interning] s3 == s1? false
[after interning]  s3 == s1? true

Проходя через это:

  1. Литерал, присвоенный s1, автоматически интернируется, поэтому s1 в итоге ссылается на строку в пуле.
  2. Литерал, присвоенный s2, также автоматически интернируется, и поэтому s2 заканчивается указанием на тот же экземпляр, на который указывает s1. Это хорошо, даже если два бита кода могут быть совершенно неизвестны друг другу, поскольку экземпляры String в Java неизменны . Вы не можете их изменить. Вы можете использовать такие методы, как toLowerCase, чтобы вернуть новую строку с изменениями, но оригинал, который вы назвали toLowerCase (и т. Д.), Остается неизменным. Таким образом, они могут быть безопасно разделены между несвязанным кодом.
  3. Мы создаем новый String экземпляр с помощью операции времени выполнения. Хотя новый экземпляр имеет ту же последовательность символов, что и интернированный, это отдельный экземпляр. Среда выполнения не обрабатывает динамически созданные строки автоматически, потому что это сопряжено с определенными затратами: работа по поиску строки в пуле. (Принимая во внимание, что при компиляции компилятор может взять эту стоимость на себя.) Итак, теперь у нас есть два экземпляра: один s1 и s2, на который указывает, и один s3, на который указывает. Таким образом, код показывает, что s3 != s1.
  4. Тогда мы явно интернируем s3. Возможно, это большая строка, которую мы планируем удерживать в течение длительного времени, и мы думаем, что, скорее всего, она будет продублирована в других местах. Таким образом, мы принимаем работу по интернированию в обмен на потенциальную экономию памяти. Поскольку интернирование по определению означает, что мы можем получить новую ссылку, мы присваиваем результат обратно s3.
  5. И мы можем видеть, что действительно, s3 теперь указывает на тот же экземпляр s1, а s2 указывают на.
10 голосов
/ 05 июля 2011

Жестко закодированные строки скомпилированы в таблицу строк JVM, которая содержит уникальные строки - то есть компилятор хранит только одну копию «Hi», поэтому вы сравниваете один и тот же объект , поэтому == работает.

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

1 голос
/ 05 июля 2011

В Java есть строковый кеш. Где, как и в этом случае, тот же объект возвращается из кэша, который содержит ту же ссылку.

0 голосов
/ 05 июля 2011

Основная причина этого в том, что "Hi" взято из String Pool.Неизменяемый объект должен иметь своего рода кэш, чтобы он мог работать лучше.Таким образом, класс String является неизменным и использует String Pool для основного кэша.

В этом случае "Hi" находится в пуле строк, и все строки, имеющие значения "Hi", будут иметь одинаковую ссылкудля струнного пула.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...