Что такое пул строк Java?
Как следует из названия, пул строк в Java — это пул строк, хранящихся в памяти кучи Java. Мы знаем, что String — это особый класс в java, и мы можем создавать объекты String, используя оператор new, а также предоставляя значения в двойных кавычках.
Пул строк в Java
package com.journaldev.util; public class StringPool < /** * Java String Pool example * @param args */ public static void main(String[] args) < String s1 = "Cat"; String s2 = "Cat"; String s3 = new String("Cat"); System.out.println("s1 == s2 :"+(s1==s2)); System.out.println("s1 == s3 :"+(s1==s3)); >>
Вывод вышеуказанной программы:
s1 == s2 :true s1 == s3 :false
Рекомендуем прочитать: класс Java String
Сколько строк создается в пуле строк?
Иногда на java-интервью вам задают вопрос о пуле строк. Например, сколько строк создается в приведенном ниже выражении;
String str = new String("Cat");
В приведенном выше выражении будет создана либо 1, либо 2 строки. Если в пуле уже есть строковый литерал \Cat, то в пуле будет создана только одна строка \str. Если в пуле нет строкового литерала \Cat, то он будет сначала создан в пуле, а затем в пространстве кучи, поэтому всего будет создано 2 строковых объекта. Читайте: Java String Interview Questions
Главная
Пул строк — это конкретная реализация JVM концепции string interning.
Когда мы используем двойные кавычки для создания строки, сначала ищется строка в пуле с таким же значением, если находится, то просто возвращается ссылка, иначе создается новая строка в пуле, а затем возвращается ссылка .
Пул строк возможен исключительно благодаря неизменяемости строк в Java и реализации идеи интернирования строк. Пул строк также является примером паттерна Приспособленец (Flyweight).
когда мы используем оператор new , мы принуждаем класс String создать новый объект строки, а затем мы можем использовать метод intern() для того, чтобы поместить строку в пул, или получить из пула ссылку на другой объект String с таким же значением.
Когда метод intern() вызван, если пул строк уже содержит строку, эквивалентную к нашему объекту, что подтверждается методом equals (Object), тогда возвращается ссылка на строку из пула. В противном случае объект строки добавляется в пул и ссылка на этот объект возвращается.
Сборка мусора :
До Java 7 , пул Java String помещался в пространство PermGen , которое имеет фиксированный размер — его нельзя развернуть во время выполнения и не подходит для сборки мусора .
Риск интернирования строк в PermGen (вместо кучи ) заключается в том, что мы можем получить ошибку OutOfMemory от JVM, если интернируем слишком много строк .
Начиная с Java 7, пул Java String хранится в пространстве Heap , собираемым JVM . Преимущество этого подхода заключается в уменьшении риска ошибки OutOfMemory, поскольку строки, на которые нет ссылок, будут удалены из пула, тем самым освобождая память.
работа пула строк :
public class MyStringPool < public static void main(String[] args) < String s1 = "Cat"; // создаем строку и помещаем ее в pool строк String s2 = "Cat"; // создаем строку и помещаем ее в pool строк String s3 = new String("Cat"); // создаем новый обьект (не помещаем его в pool) System.out.println("s1 == s2 :"+(s1==s2)); System.out.println("s1 == s3 :"+(s1==s3)); > > s1 == s2 :true s1 == s3 :false
*.intern() :
String str1 = "first"; String str2 = "first"; String str3 = new String("first"); str3 = str3.intern(); System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str3); true true true
Полезные ссылки:
- «str».intern() in JavaDoc (Java 8)
- «str».intern() in JavaDoc (Java 7)
- «str».intern() in JavaDoc (Java 6)
Разбираемся со строками в Java и Kotlin

Цель сегодняшнего поста раз и навсегда разобраться с таким типом данных как String. Вы все еще конкатенируете строки и не знаете зачем нужен StringBuilder? Тогда не проходите мимо – полученные знания помогут вам при собеседовании – такие вопросы часто любят задавать.
String Pool
Класс String представляет собой массив char. Каждый char занимает 2 байта. Кроме этого класс String имеет кэшируемый хэш, который занимает еще 4 байта. Кроме того, каждый объект имеет служебную информацию которая занимает 8 байт. И, если мы говорим о Java Development Kit 7 или ниже, то у класса String есть еще поля offset и length. Так класс String наиболее используемый типа данных, то многие инстансы таких объектов могут занимать значительную часть памяти. Поэтому, для сокращения потребляемой памяти JVM имеет, так называемый String pool, который представляет собой имплементацию паттерна проектирования Flyweight – шаблон проектирования, при котором объект, представляющий себя как уникальный экземпляр в разных местах программы, по факту не является таковым, так как размер потребляемой памяти может быть критичным для девайсов с маленьким размером свободной памяти, таких как мобильные устройства.
Создавая новую строку (используя двойные кавычки “”), JVM сначала ищет инстанс такой же строки (у которой такое же значение) в String pool. Если такое же значение найдено, то возвращается ссылка на это значение. В противном случае – создается новый инстанс в String pool и возвращается ссылка на него. Используя же конструктор, мы создаем новый инстанс в heap, а не в String pool:

Эта техника называется copy-on-write (COW). Идея в том, что когда мы вызываем необходимый объект, то возвращается ссылка на уже существующий вместо создания нового. Пример:
fun main(vars: Array) val cat1 = "Cat" val cat2 = "Cat" val cat3 = String("Cat".toCharArray()) println(cat1 === cat2) println(cat1 === cat3) >
Результат такого кода:
truefalse
При COW, когда мы пытаемся модифицировать объект через ссылку, то создается копия объекта, она изменяется и после этого возвращается ссылка на новый созданный объект.

fun main(vars: Array)
Cat CatDog false
Теперь, зная как работает JVM при изменении строки, рассмотрим такой код:
class User(val id: Int = 0, val firstName: String = "", val lastName: String = "") fun main(vars: Array) < val user = User() val building = "304a" val query = "SELECT id, firstName, lastName FROM Building " + building + " WHERE firstName color:#000000;font-size:18px" >При каждой конкатенации будет создаваться объект, поэтому лучше всего использовать StringBuilder или String Templates в Kotlinval query = "SELECT id, firstName, lastName FROM Building $building WHERE firstName = $"А теперь, фишка, о которой мало кто знает, но вы можете блеснуть на собеседовании. У класса String есть метод intern(). Метод intern() перед созданием объекта String смотрит есть ли этот объект в String Pool и возвращает его. Иначе создается новый объект в String Pool. Таким образом вы можете сохранить строку, созданную в куче в пуле строк. Пример:
fun main(args: Array)
Данный код вернет false
А код ниже уже вернет true
fun main(args: Array)
Вот так! Надеюсь эта статья была для вас полезной. Пишите комментарии в ВК и подписывайтесь на telegram-канал.
Java Challengers #2: Сравнение строк
У нас как всегда много опаздывающих к началу курса, так что только вчера провели второе занятие среди нового потока "Разработчик Java". Но это так, мелочи жизни, а пока что мы продолжаем публикацию серии статей Java Challengers, перевод которых подготовили для вас.
В Java класс String инкапсулирует массив char (прим. переводчика — с java 9 это уже массив byte , см. Компактные строки в Java 9). Говоря по простому, String — это массив символов, используемый для составления слов, предложений или других конструкций.
Инкапсуляция — это одна из самых мощных концепций объектно — ориентированного программирования. Благодаря инкапсуляции вам не нужно знать как работает класс String . Вам достаточно знать методы его интерфейса.
Когда вы смотрите на класс String в Java, вы можете увидеть как инкапсулирован массив char :
public String(char value[])Чтобы лучше понять инкапсуляцию, представьте физический объект: машину. Нужно ли вам знать, как работает автомобиль под капотом, чтобы управлять им? Конечно, нет, но вы должны знать, что делают интерфейсы автомобиля: педаль газа, тормоза и рулевое колесо. Каждый из этих интерфейсов поддерживает определенные действия: ускорение, торможение, поворот налево, поворот направо. То же самое и в объектно — ориентированном программировании.
Первая статья в серии Java Challengers была про перегрузку методов, которая широко используется в классе String . Перегрузка может сделать ваши классы действительно гибкими:
public String(String original) <> public String(char value[], int offset, int count) <> public String(int[] codePoints, int offset, int count) <> public String(byte bytes[], int offset, int length, String charsetName) <> // И так далее .Вместо того, чтобы пытаться понять, как работает класс String , эта статья поможет вам понять что он делает и как использовать его в вашем коде.
Что такое пул строк (String pool)
Класс String , возможно, наиболее часто используемый класс в Java. Если новый объект создавать в динамической памяти (memory heap) каждый раз, когда мы используем String , то мы потратим впустую много памяти. Пул строк (String pool) решает эту проблему, сохраняя только один объект для каждого значения строки.
Строки в пуле строк
Хотя мы создали несколько переменных String со значениями Duke и Juggy , но в динамической памяти (куче) создаётся и храниться только два объекта. Для доказательства посмотрите следующий пример кода. (Напомним, что в Java оператор " == " используется для сравнения двух объектов и определения того один и тот же это объект или нет.)
String juggy = "Juggy"; String anotherJuggy = "Juggy"; System.out.println(juggy == anotherJuggy);Этот код вернет true , потому что две переменные String указывают на один и тот же объект в пуле строк. Их значения одинаковые.
Исключение — оператор new
Теперь посмотрите на этот код — он выглядит похожим на предыдущий пример, но здесь есть отличие.
String duke = new String("duke"); String anotherDuke = new String("duke"); System.out.println(duke == anotherDuke);На основе предыдущего примера можно подумать, что этот код вернёт true , но это не так. Добавление оператора new приводит к созданию нового объекта String в памяти. Таким образом, JVM создаст два разных объекта.
Native-методы в Java — это методы, которые будут компилироваться с использованием языка C, обычно с целью управления памятью и оптимизации производительности.
Пулы строк и метод intern()
Для хранения строк в пуле используется способ, называемый "интернирование строк" (String interning).
Вот, что Javadoc говорит нам о методе intern() :
/** * Возвращает каноническое представление для строкового объекта. * * Пул строк (первоначально пустой) управляется классом . * * Когда вызывается метод intern, если пул уже содержит строку, * равную этому объекту , определяемому через * метод , тогда возвращается строка из пула. * Иначе, этот объект добавляется к * пулу и возвращается ссылка на этот объект . * * Из этого следует, что для любых двух строк и , * будет * тогда и только тогда, когда равно . * * Все литеральные строки и строковые константы интернируются. * Строковые литералы определяются в разделе 3.10.5 The Java™ Language Specification. * * @returns строка, которая имеет то же самое содержание как эта строка, * но, гарантируется, что она будет из пула уникальных строк. * * @jls 3.10.5 String Literals */ public native String intern();Метод intern() используется для хранения строк в пуле строк. Во-первых, он проверяет, существует ли уже созданная строка в пуле. Если нет, то создает новую строку в пуле. Логика пула строк основана на паттерне Flyweight.
Теперь, обратите внимание, что происходит, когда мы используем new для создания двух строк:
String duke = new String("duke"); String duke2 = new String("duke"); System.out.println(duke == duke2); // Здесь результат будет false System.out.println(duke.intern() == duke2.intern()); // Здесь результат будет trueВ отличие от предыдущего примера с ключевым словом new , в данном случае сравнение вернёт true . Это потому, что использование метода intern() гарантирует, что строка будет в пуле.
Метод equals в классе String
Метод equals() используется для того, чтобы проверить одинаковое или нет состояние двух классов. Поскольку equals() находится к классе Object , то каждый Java — класс наследует его. Но метод equals() должен быть переопределен, чтобы он работал правильно. Конечно, String переопределяет equals() .
public boolean equals(Object anObject) < if (this == anObject) < return true; >if (anObject instanceof String) < String aString = (String)anObject; if (coder() == aString.coder()) < return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); >> return false; >Как вы видите, значение класса String сравнивается через equals() , а не через ссылку на объект. Не имеет значения, если ссылки на объекты разные; будут сравниваться состояния.
Наиболее распространенные методы String
Есть ещё одна вещь, которую вам нужно знать, прежде чем решить задачку на сравнение строк.
Рассмотрим наиболее распространённые методы класса String :
// Удаляет пробелы в начале и в конце строки trim() // Получает подстроку по индексам substring(int beginIndex, int endIndex) // Возвращает длину строки length() // Заменяет строку, можно использовать регулярное выражение replaceAll(String regex, String replacement) // Проверяет, есть ли указанная последовательность CharSequence в строке contains(CharSequences)Решите задачку на сравнение строк
Давайте проверим, что вы узнали о классе String , решив небольшую задачку.
В этой задаче вы сравните несколько строк, используя изученные концепции. Глядя на код ниже, можете ли вы определить значение каждой переменной result ?
public class ComparisonStringChallenge < public static void main(String. doYourBest) < String result = ""; result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; result += "flexibleCode" == "flexibleCode" ? "2" : "3"; result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; System.out.println(result); >>Какой будет вывод?
Правильный ответ приведён в конце статьи.
Что сейчас произошло? Понимание поведения String
В первой строке мы видим:
result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1";В этом случае результат false , потому что, когда метод trim() удаляет пробелы он создаёт новый String с помощью оператора new .
result += "flexibleCode" == "flexibleCode" ? "2" : "3";Здесь нет никакой тайны, строки одинаковы в пуле строк. Это сравнение возвращает true .
result += new String("doYourBest") == new String("doYourBest") ? "4" : "5";Использование new приводит к созданию двух новых строк и не важно равны их значения или нет. В этом случае сравнение будет false даже если значения одинаковые.
result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7";Поскольку мы использовали метод equals() , будет сравниваться значение строки, а не экземпляр объекта.
В этом случае, не имеет значение разные объекты или нет, поскольку сравнивается значение. Результат true .
Окончательно, мы имеем:
result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9";Как вы видели ранее, метод intern() помещает строку в пул строк. Обе строки указывают на один и тот же объект, поэтому в этом случае true .
Распространенные ошибки со строками
Бывает трудно определить, указывают ли две строки на один и тот же объект или нет, особенно когда строки содержат одно и то же значение. Полезно помнить, что использование new всегда приводит к созданию нового объекта в памяти, даже если значения строк одинаковые.
Использование методов класса String для сравнения ссылок на объекты также может быть сложным. Особенность в том, что если метод изменяет что-то в строке, то будут разные ссылки на объекты.
Несколько примеров, которые помогут прояснить:
System.out.println("duke".trim() == "duke".trim());Это сравнение будет истинным, потому что метод trim() не создает новую строку.
System.out.println(" duke".trim() == "duke".trim());В этом случае первый метод trim() генерирует новую строку, так как метод будет выполнять свою работу и поэтому ссылки будут разные.
Наконец, когда trim() выполнит свою работу, он создает новую строку:
// Реализация метода trim в классе String new String(Arrays.copyOfRange(val, index, index + len), LATIN1);Что нужно помнить о строках
- Строки не изменяемые, поэтому состояние строки изменить нельзя.
- Для экономии памяти JVM хранит строки в пуле строк. При создании новой строки JVM проверяет ее значение и указывает на существующий объект. Если в пуле нет строки с этим значением, то JVM создаёт новую строку.
- Оператор " == " сравнивает ссылки на объект. Метод equals() сравнивает значения строк. То же правило будет применяться ко всем объектам.
- При использовании оператора new будет создана новая строка в хипе (Прим. переводчика — в оригинале написано, что в пуле, но это не так, спасибоzagayevskiy), даже если есть строка с тем же значением.
Ответ
Ответ на эту задачу — D. Вывод будет 12568.


