По каким критериям в Java строка попадает в пул строк?

236
07 сентября 2017, 16:57

По сути этот вопрос продолжает другой вопрос.

Во многих статьях говорится, что при создании строк без new через литерал строка попадает в пул строк. В противном случае необходим метод intern. Как тогда объяснить поведение ниже ?

    String s2 = "hello";
    String s1 = "hello";
    System.out.println(s1 == s2); // true
    System.out.println("hel" + "lo" == "hello"); // true
    s1 = "hello";
    s2 = "hel";
    String s3 = "lo";
    System.out.println(s1 == s2 + s3); // false
    System.out.println(s2 + s3 == "hel" + "lo"); //false

Если в последнем случае s2 ссылается туда же куда и литерал "hel", и с s3 такая же история, то почему "hel" + "lo" не равно (по ссылке) s2 + s3 ?

Answer 1

Строка может попасть в пул констант в двух случаях:

  1. На этапе компиляции.

Компилятор проходит по всем строкам в исходном коде и добавляет их в пул констант. Если в коде есть какие-то места, в которых нужны вычисления и компилятор может их произвести, то он делает это. В Вашем примере это System.out.println("hel" + "lo" == "hello");. Сконкатенировал "hel" + "lo" и положил в пул.

  1. Вызов String.intern()

Описание метода (ссылка на документацию). Здесь надо обязательно указать границы применения данного способа. Данный методы выглядит очень заманчиво: "мы можем сократить использование памяти, и производительность в програме!! Это громадная оптимизация!!"

  • Из intern пула НЕ возможно удалить данные
  • intern медленнее чем реализация (через ConcurrentHashMap, например) интернирования. Потому что во внутренней таблице (в которой хранятся интерниованные строки) всего около 60 000 бакетов и большОе количество строк может забить эти бакеты. Покачто эта Map не ресайзится.
  • intern может влиять на работу всей JVM так как таблица используется для хранения информации о классах

Если мне не верите, то Алексею Шипилеву должны поверить (про intern с 32 минуты).

Answer 2

Строка попадет в пул строк только после вызова метода intern класса String.

Детально про строковые литералы из спецификации на английском

По поводу "hel" + "lo" - это строковая константа

Возьмем класс

   public static void main(String[] args) {
        String s2 = "hel";
        String s3 = "lo";
        System.out.println(s2 + s3 == "hel" + "lo");
    }

Скомпилируем и посмотрим его декомпилятором

public static void main(String[] args) {
    String s2 = "hel";
    String s3 = "lo";
    System.out.println(s2 + s3 == "hello");
}

Компилятор собрал "hel" + "lo" в одну строку, что бы доказать это, посмотрим байткод:

public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    LDC "hel"
    ASTORE 1
   L1
    LINENUMBER 7 L1
    LDC "lo"
    ASTORE 2
   L2
    LINENUMBER 9 L2
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    LDC "hello"
    IF_ACMPNE L3
    ICONST_1
    GOTO L4
   L3
   FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String] [java/io/PrintStream]
    ICONST_0
   L4
   FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String] [java/io/PrintStream I]
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
   L5
    LINENUMBER 10 L5
    RETURN
   L6
    LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
    LOCALVARIABLE s2 Ljava/lang/String; L1 L6 1
    LOCALVARIABLE s3 Ljava/lang/String; L2 L6 2
    MAXSTACK = 3
    MAXLOCALS = 3
}

"hello" представлена константой, s2 + s3 собираются через StringBuilder и преобразуются в строку через toString(), соответственно это 2 разных объекта, и при сравнении указателей будет false

READ ALSO
Написание SDK для браузерной игры [требует правки]

Написание SDK для браузерной игры [требует правки]

ЗдравствуйтеПишу неофициальный SDK для браузерной игры, который разработчики смогут встраивать в свой проект

204
Что означает данная строка?

Что означает данная строка?

Вот метод parse(String) из класса DateUtil:

245
Кодировка пути файла

Кодировка пути файла

Приветствую всех!

206
Вывод данных из БД при загрузке JSP

Вывод данных из БД при загрузке JSP

Мне нужно, чтобы в начальной loginjsp,

179