Странное поведение с интернетом строк в Java

Имеется следующий код:

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);

String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

Вывод кода выше:

false
true

Я знаю, что s и s2 - разные объекты, поэтому результат оценивается как false, но второй результат равен true. Может ли кто-нибудь сказать мне разницу?

Ответ 1

Вот что происходит:


Пример 1

String s1 = new String("1"); 
s1.intern();
String s2 = "1";
  • Строковый литерал "1" (переданный в конструктор String) интернирован по адресу A.
    Строка s1 создается по адресу B, потому что это не буквальное или постоянное выражение.
  • Вызов intern() не действует. Строка "1" уже интернирована, и результат операции не привязан к s1.
  • String s2 со значением "1" извлекается из пула строк, поэтому указывает на адрес A.

Результат: Строки s1 и s2 указывают на разные адреса.


Пример 2

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
  • Строка s3 создается по адресу C.
  • Вызов intern() добавляет строку со значением "11" по адресу C в пул строк.
  • String s4 со значением "11" извлекается из пула строк, поэтому указывает на адрес C.

Результат: Строки s3 и s4 указывают на тот же адрес.


Резюме

String "1" интернируется до вызова intern(), в силу своего присутствия в вызове конструктора s1 = new String("1").

Изменение этого вызова конструктора на s1 = new String(new char[]{'1'}) приведет к тому, что сравнение s1 == s2 будет равно true, так как теперь оба будут ссылаться на строку, которая была явно интернирована, вызывая s1.intern().

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

Ответ 2

Для сценария 1:

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);

с байт-кодом:

   0: new           #2                  // class java/lang/String
   3: dup
   4: ldc           #3                  // String 1
   6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
   9: astore_1
  10: aload_1
  11: invokevirtual #5                  // Method java/lang/String.intern:()Ljava/lang/String;
  14: pop
  15: ldc           #3                  // String 1

для String s = new String("1"); он создаст новый объект String, у него будет новый адрес с "1" , который уже находится в String Pool:

ldc #3 // String 1

и для s2, в качестве байт-кода:

15: ldc #3 // String 1

s2 указывает на переменную String Pool: "1", поэтому s и s2 имеют другой адрес, а результат - false.

Для сценария 2:

String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

с байт-кодом:

   0: new           #2                  // class java/lang/StringBuilder
   3: dup
   4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
   7: astore_1
   8: aload_1
   9: ldc           #4                  // String 1
  11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  14: ldc           #4                  // String 1
  16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  19: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  22: astore_2
  23: aload_2
  24: invokevirtual #7                  // Method java/lang/String.intern:()Ljava/lang/String;
  27: astore_3
  28: ldc           #8                  // String 11

Как байт-код, вы можете видеть, что new String("1")+new String("1"); создается с помощью StringBuilder

new #2 // class java/lang/StringBuilder

это полностью новый объект без переменной String Pool.

и после s3.intern() этот метод добавит текущий s3 в Пул строк памяти и 8: aload_1.

и s4 пытается загрузить из

ldc #8 // String 11

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

Ответ 3

Просто для тех, кто использует groovy, информация о добавлении: поведение отличается

введите описание изображения здесь

Ответ 4

s.intern() не изменяет строку s. Вы должны были написать:

    s = s.intern();