Страницы

Поиск по вопросам

воскресенье, 22 декабря 2019 г.

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

#java


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

Во многих статьях говорится, что при создании строк без 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 ? 
    


Ответы

Ответ 1



Строка может попасть в пул констант в двух случаях: На этапе компиляции. Компилятор проходит по всем строкам в исходном коде и добавляет их в пул констант. Если в коде есть какие-то места, в которых нужны вычисления и компилятор может их произвести, то он делает это. В Вашем примере это System.out.println("hel" + "lo" == "hello");. Сконкатенировал "hel" + "lo" и положил в пул. Вызов String.intern() Описание метода (ссылка на документацию). Здесь надо обязательно указать границы применения данного способа. Данный методы выглядит очень заманчиво: "мы можем сократить использование памяти, и производительность в програме!! Это громадная оптимизация!!" Из intern пула НЕ возможно удалить данные intern медленнее чем реализация (через ConcurrentHashMap, например) интернирования. Потому что во внутренней таблице (в которой хранятся интерниованные строки) всего около 60 000 бакетов и большОе количество строк может забить эти бакеты. Покачто эта Map не ресайзится. intern может влиять на работу всей JVM так как таблица используется для хранения информации о классах Если мне не верите, то Алексею Шипилеву должны поверить (про intern с 32 минуты).

Ответ 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. ()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

Комментариев нет:

Отправить комментарий