Выражение сравнения concat=="string" возвращает false как очевидное (я понимаю разницу между equals() и ==).
Когда эти две строки объявлены final вот так,
final String str1="str"; final String str2="ing"; String concat=str1+str2;
System.out.println(concat=="string");
Выражение сравнения concat=="string" в этом случае возвращает true. Почему final имеет значение? Это должно как-то быть связано с пулом стажеров или меня просто вводят в заблуждение?
Переведено автоматически
Ответ 1
Когда вы объявляете String (которая является неизменяемой) переменной как final и инициализируете ее с помощью выражения-константы времени компиляции, она также становится выражением-константой времени компиляции, и ее значение встроено компилятором, в котором она используется. Итак, во втором примере вашего кода после встраивания значений конкатенация строк преобразуется компилятором в:
Stringconcat="str" + "ing"; // which then becomes `String concat = "string";`
что при сравнении с "string" даст вам true, потому что строковые литералы интернированы.
Переменная примитивного типа или type String, которая является final и инициализируется константным выражением во время компиляции (§15.28), называется постоянной переменной.
Константные выражения типа во время компиляции String всегда "интернированы", чтобы совместно использовать уникальные экземпляры, используя метод String#intern().
Это не тот случай в вашем первом примере кода, где String переменные не являются final. Таким образом, они не являются постоянными выражениями во время компиляции. Операция конкатенации там будет отложена до времени выполнения, что приведет к созданию нового String объекта. Вы можете убедиться в этом, сравнив байтовый код обоих фрагментов кода.
Первый пример кода (неfinal версия) компилируется в следующий байтовый код:
Таким образом, он напрямую вводит переменную final для создания строки string во время компиляции, которая загружается с помощью ldc операции на шаге 0. Затем второй строковый литерал загружается с помощью ldc операции на шаге 7. Это не требует создания какого-либо нового String объекта во время выполнения. Строка уже известна во время компиляции, и они интернированы.
Ответ 2
Согласно моему исследованию, все final String интернированы в Java. Из одного из сообщений в блоге:
Итак, если вам действительно нужно сравнить две строки с помощью == или != убедитесь, что вы вызываете метод String.intern() перед выполнением сравнения. В противном случае всегда отдавайте предпочтение String.equals(Строка) для сравнения строк.
Таким образом, это означает, что при вызове String.intern() вы можете сравнить две строки с помощью == оператора. Но здесь String.intern() в этом нет необходимости, потому что в Java final String они внутренне интернированы.
это означает, что concat будет создано во время выполнения, поэтому не будет получено из пула строк.
Также, если строки являются final, компилятор может предположить, что они никогда не изменятся, поэтому вместо использования StringBuilder он может безопасно объединить свои значения таким образом
Stringconcat= str1 + str2;
может быть изменено на
Stringconcat="str" + "ing";
и объединены в
Stringconcat="string";
это означает, что concate станет строковым литералом, который будет интернирован в пул строк, а затем сравнен с тем же строковым литералом из этого пула в if инструкции.