Неизменяемость String и пул строк
Поймите, почему Strings неизменяемы, как работает пул строк и когда использовать ==, а когда equals.
«Неизменяемость String и пул строк» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Неизменяемость String и пул строк
Строки в Java неизменяемы. Понимание неизменяемости, пула строк и различия между равенством ссылок и равенством значений помогает предотвращать трудно обнаружимые ошибки.
Почему строки неизменяемы
Объекты String нельзя изменить после создания. Любая операция, которая якобы «изменяет» String, на самом деле создаёт новый объект String. Это обеспечивает sharing, потокобезопасность и кэширование hashCode.
String s = "hello";
s.toUpperCase(); // creates a new String, does NOT modify s
System.out.println(s); // still "hello"
String upper = s.toUpperCase(); // must capture the result
System.out.println(upper); // HELLOПул строк (интернирование)
Строковые литералы хранятся в пуле String (специальной области кучи). Два литерала с одинаковым содержимым используют один и тот же объект.
String a = "hello"; // goes into pool
String b = "hello"; // same object from pool
String c = new String("hello"); // forces new object outside pool
System.out.println(a == b); // true (same pool reference)
System.out.println(a == c); // false (different objects)
System.out.println(a.equals(c)); // true (same content)== и equals()
Для сравнения содержимого String всегда используйте .equals(). Оператор == проверяет, указывают ли две переменные на один и тот же объект (равенство ссылок).
String input = new String("admin"); // from user input, not pooled
String role = "admin";
// Dangerous: fails because different objects
if (input == role) System.out.println("Same"); // not printed!
// Correct: compares content
if (input.equals(role)) System.out.println("Equal!"); // prints
// Null-safe: put constant first to avoid NPE
if ("admin".equals(input)) System.out.println("Admin"); // printsintern(): ручное помещение String в пул
String.intern() принудительно помещает String в пул и возвращает ссылку на объект из пула. В современной Java это требуется редко.
String s1 = new String("interned");
String s2 = s1.intern();
String s3 = "interned"; // already in pool
System.out.println(s2 == s3); // true (both point to pool)
System.out.println(s1 == s3); // false (s1 is off-pool)
// Modern use: prefer String.intern() only when you need
// identity-based caching with thousands of repeated stringsПоведение конкатенации String
Конкатенация с помощью + создаёт новые объекты String. Внутри цикла это имеет сложность O(n²). Компилятор оптимизирует отдельные выражения, но не конкатенацию в циклах.
// Compiler optimizes this to a StringBuilder automatically:
String result = "Hello, " + "World" + "!";
// In loops: compiler does NOT optimize — use StringBuilder explicitly!
List<String> words = List.of("a", "b", "c", "d");
String bad = ""; // O(n^2) — new object each iteration
for (String w : words) bad += w;
StringBuilder sb = new StringBuilder();
for (String w : words) sb.append(w); // O(n) — efficient
String good = sb.toString();
System.out.println(good); // abcdПравильное сравнение строк
Всегда используйте equals(); для сравнения без учёта регистра используйте equalsIgnoreCase(). Для лексикографической сортировки используйте compareTo().
String a = "Java";
String b = "java";
System.out.println(a.equals(b)); // false
System.out.println(a.equalsIgnoreCase(b)); // true
System.out.println(a.compareToIgnoreCase(b)); // 0 (equal)
System.out.println(a.compareTo(b)); // negative (J < j in Unicode)Кэширование hashCode в String
Поскольку строки неизменяемы, Java кэширует hashCode после первого вызова. Последующие вызовы возвращают кэшированное значение, поэтому строки очень эффективны в качестве ключей HashMap.
String key = "product:12345";
// hashCode computed once and cached:
int h1 = key.hashCode();
int h2 = key.hashCode(); // returns cached value
System.out.println(h1 == h2); // true
// Strings are ideal HashMap keys because:
// 1. hashCode is consistent (immutable content)
// 2. hashCode is cached (fast repeated lookups)
// 3. equals is well-defined (content-based)
Map<String, Integer> map = new HashMap<>();
map.put("key", 42);
System.out.println(map.get("key")); // 42String — конечный класс
Класс String объявлен как final — от него нельзя наследоваться. Это гарантирует, что ни один подкласс не сможет нарушить неизменяемость или согласованность hashCode.
// String is final — this will not compile:
// class MutableString extends String {} // compile error
// You cannot override String behavior.
// If you need custom string-like behavior, use a wrapper:
record ProductSku(String value) {
ProductSku { if (!value.matches("[A-Z]{3}-[0-9]{4}")) throw new IllegalArgumentException(); }
@Override public String toString() { return value; }
}String.format и конкатенация
Для строк из нескольких частей предпочитайте String.format() или текстовые блоки конкатенации — так код понятнее и его проще локализовать.
String name = "Alice";
int age = 30;
double score = 97.5;
// Concatenation — harder to read with many parts
String bad = "User: " + name + ", Age: " + age + ", Score: " + score;
// String.format — clear placeholders
String good = String.format("User: %s, Age: %d, Score: %.1f", name, age, score);
System.out.println(good);
// User: Alice, Age: 30, Score: 97.5Операции со String с безопасной обработкой null
Многие операции String выбрасывают NPE, если строка равна null. Для безопасной обработки null используйте защитные шаблоны или String.valueOf().
String value = null;
// Null-safe length check
int len = (value != null) ? value.length() : 0;
System.out.println(len); // 0
// Null-safe comparison (put constant first)
boolean isAdmin = "admin".equals(value); // false, no NPE
// String.valueOf converts null to "null" string
System.out.println(String.valueOf(value)); // "null"
System.out.println(Objects.toString(value, "default")); // "default"Быстрая проверка
Что выведет следующий код?
String a = "hello";
String b = new String("hello");
System.out.println(a.equals(b));
System.out.println(a == b);Итоги: неизменяемость String и пул строк
Главные выводы:
- Строки неизменяемы — операции возвращают новые объекты String и никогда не изменяют исходный объект
- Строковые литералы помещаются в пул — два литерала с одинаковым содержимым используют один объект
- Для сравнения содержимого String всегда используйте .equals(); оператор == сравнивает ссылки на объекты
- Для сравнений без учёта регистра используйте equalsIgnoreCase()
- Конкатенация строк в циклах имеет сложность O(n²) — для эффективности используйте StringBuilder
- Строки кэшируют свой hashCode, поэтому они эффективны и безопасны в качестве ключей HashMap
Часто задаваемые вопросы
Урок «Неизменяемость String и пул строк» бесплатный?
Да — полный текст урока «Неизменяемость String и пул строк» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Неизменяемость String и пул строк»?
Поймите, почему Strings неизменяемы, как работает пул строк и когда использовать ==, а когда equals. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Неизменяемость String и пул строк»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Неизменяемость String и пул строк
- StringBuilder для эффективного объединения
- Текстовые блоки и String.format
- Регулярные выражения с Pattern и Matcher