← Назад к вопросам
Что происходит при использовании new String
2.0 Middle🔥 111 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что происходит при использовании new String
new String — это один из самых часто задаваемых вопросов на интервью Java разработчиков, потому что он касается фундаментального понимания того, как работают строки и управление памятью в Java.
1. Основной механизм
public class StringCreation {
public static void main(String[] args) {
// При выполнении этой строки происходит ДВЕ операции:
String s = new String("Hello");
// Шаг 1: Литерал "Hello" добавляется в String pool
// (если там уже нет такого значения)
// Шаг 2: Создаётся НОВЫЙ объект String в heap
// Шаг 3: Переменная s ссылается на объект в heap
}
}
// Визуализация памяти:
//
// Heap: String Pool:
// +-----------+ +-----------+
// | String |------------> | "Hello" |
// | object | +-----------+
// +-----------+ (new String) (существует в pool)
// ^
// |
// s
2. Сравнение способов создания строк
public class StringCreationMethods {
public static void main(String[] args) {
// Метод 1: Строковый литерал
String s1 = "Hello";
// Результат: объект в String pool
// Место: String pool (константная область памяти)
// Память: экономна (переиспользуется)
// Метод 2: new String с литералом
String s2 = new String("Hello");
// Результат: ДВА объекта
// - Один в String pool ("Hello")
// - Один в heap (новый объект String)
// Место: heap + String pool
// Память: тратится на два объекта
// Метод 3: new String с конкатенацией
String s3 = new String("Hel" + "lo");
// Результат: компилятор оптимизирует "Hel" + "lo" в "Hello"
// Потом создаётся объект в heap
// Метод 4: new String из массива символов
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String s4 = new String(chars);
// Результат: новый объект String в heap
// Не добавляется в String pool
// Сравнение ссылок
System.out.println(s1 == s2); // false! (разные объекты)
System.out.println(s1.equals(s2)); // true! (одинаковое содержимое)
}
}
3. String Pool подробно
public class StringPoolMechanism {
public static void main(String[] args) {
// String pool - это таблица хэша, где хранятся уникальные String значения
String a = "test"; // Добавляется в pool
String b = "test"; // Берётся из pool (s1 и s2 указывают на ОДИН объект)
String c = new String("test"); // Создаёт НОВЫЙ объект в heap
System.out.println(a == b); // true (одна ссылка на pool)
System.out.println(a == c); // false (разные объекты)
System.out.println(a.equals(c)); // true (одинаковое содержимое)
// Явное добавление в pool
String d = new String("test");
String e = d.intern(); // Добавляет в pool и возвращает ссылку
System.out.println(a == e); // true (теперь указывают на pool)
System.out.println(d == e); // false (d всё ещё в heap)
}
}
4. Конструкторы String
public class StringConstructors {
public static void main(String[] args) {
// 1. String(String original)
String s1 = new String("Hello");
// Копирует содержимое из original
// Создаёт новый объект в heap
// 2. String(char[] value)
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String s2 = new String(chars);
// Создаёт String из массива символов
// 3. String(char[] value, int offset, int count)
String s3 = new String(chars, 1, 3);
// Результат: "ell" (начиная с индекса 1, 3 символа)
// 4. String(byte[] bytes)
byte[] bytes = {72, 101, 108, 108, 111}; // ASCII коды
String s4 = new String(bytes);
// Результат: "Hello"
// 5. String(byte[] bytes, String charsetName)
String s5 = new String(bytes, "UTF-8");
// Декодирует байты используя UTF-8
// 6. String(StringBuffer buffer)
StringBuffer sb = new StringBuffer("Hello");
String s6 = new String(sb);
// Создаёт String из StringBuffer
// 7. String(StringBuilder builder)
StringBuilder builder = new StringBuilder("Hello");
String s7 = new String(builder);
// Создаёт String из StringBuilder
}
}
5. Производительность и утечки памяти
public class StringPerformanceIssues {
// ПЛОХО: использование new String в цикле
static void inefficientExample() {
String result = "";
for (int i = 0; i < 10000; i++) {
result = new String(result + i);
// Каждая итерация:
// 1. Создаёт промежуточную строку (result + i)
// 2. Создаёт новый String объект через new
// 3. Предыдущий объект становится мусором
// ИТОГО: ~10000 объектов String в памяти
}
}
// ХОРОШО: использование StringBuilder
static void efficientExample() {
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10000; i++) {
result.append(i);
// Один объект StringBuilder, переиспользуется
// Финальная конверсия в String один раз
}
String finalString = result.toString();
// ИТОГО: 1 объект StringBuilder + 1 финальный String
}
// Сравнение памяти
static void memoryComparison() {
Runtime runtime = Runtime.getRuntime();
// До
long before = runtime.totalMemory() - runtime.freeMemory();
// Неэффективный способ
String result = "";
for (int i = 0; i < 1000; i++) {
result = new String(result + i);
}
// После
long after = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Memory used: " + (after - before) / 1024 + " KB");
}
}
6. Неизменяемость String
public class StringImmutability {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = s1; // s2 ссылается на тот же объект
// При операции (которая выглядит как изменение)
s1 = s1 + " World";
// На самом деле:
// 1. Создаётся новая строка " World"
// 2. Создаётся новая строка результата конкатенации
// 3. s1 теперь указывает на новый объект
// 4. s2 всё ещё указывает на старый объект "Hello"
System.out.println(s1); // "Hello World"
System.out.println(s2); // "Hello"
System.out.println(s1 == s2); // false
// Это демонстрирует почему String является immutable
}
}
7. Практические примеры где new String нужен
public class StringPracticalExamples {
// 1. Копирование String для гарантии отсутствия общих ссылок
static void defensiveCopy() {
String original = "sensitive data";
String copy = new String(original);
// Теперь copy - независимая копия
// Хотя String неизменяем, это гарантирует
// что никто не может изменить исходный
}
// 2. Работа с данными из внешних источников
static void externalDataProcessing(byte[] externalData) {
String str = new String(externalData, StandardCharsets.UTF_8);
// Декодирование байтов в строку с определённой кодировкой
}
// 3. Разбор конфигурационных строк
static void parseConfiguration(String configString) {
// Может быть полезно создать новый String для гарантии
// что изменения не повлияют на исходный
String config = new String(configString);
// Обработка
}
// 4. Работа с наследованием String (редко)
static class CustomString extends String {
// Хотя String final, в старом коде может быть такое
}
}
8. Сравнение операций со строками
public class StringOperationComparison {
public static void main(String[] args) {
// Операция 1: String литерал
long start = System.nanoTime();
String s1 = "Hello";
long time1 = System.nanoTime() - start;
// Операция 2: new String
start = System.nanoTime();
String s2 = new String("Hello");
long time2 = System.nanoTime() - start;
// Операция 3: конкатенация
start = System.nanoTime();
String s3 = "Hello" + " World";
long time3 = System.nanoTime() - start;
// new String медленнее литерала
System.out.println("Literal: " + time1 + " ns");
System.out.println("new String: " + time2 + " ns");
System.out.println("Concatenation: " + time3 + " ns");
}
}
9. Когда использовать new String
// ИСПОЛЬЗУЙ new String КОГДА:
// 1. Нужна гарантия отдельного объекта
String independent = new String(sharedString);
// 2. Декодирование байтов
String fromBytes = new String(bytes, "UTF-8");
// 3. Копирование из StringBuffer/StringBuilder
String result = new String(stringBuilder);
// 4. Работа с char массивом
String fromChars = new String(charArray);
// НЕ ИСПОЛЬЗУЙ new String КОГДА:
// 1. Просто присваиваешь строку
String s = "Hello"; // А не new String("Hello")
// 2. Конкатенируешь в цикле
for (...) {
result += item; // Используй StringBuilder
}
// 3. Создаёшь множество одинаковых строк
// Они будут в pool, переиспользуются
10. Интернирование строк
public class StringInternExample {
public static void main(String[] args) {
// intern() добавляет строку в pool и возвращает ссылку
String s1 = new String("Hello");
String s2 = s1.intern(); // Добавляет в pool
String s3 = "Hello"; // Берёт из pool
System.out.println(s2 == s3); // true (обе из pool)
System.out.println(s1 == s3); // false (s1 в heap)
// Важно: intern() может быть медленным для больших строк
// используй только когда нужна гарантия уникальности
}
}
Best Practices
// 1. ПРЕДПОЧИТАЙ литералы
String good = "Hello"; // Быстро, экономно
String bad = new String("Hello"); // Медленно, тратит память
// 2. ИСПОЛЬЗУЙ StringBuilder для конкатенации
// Вместо:
String result = "";
for (Item item : items) {
result += item; // ПЛОХО
}
// Лучше:
StringBuilder sb = new StringBuilder();
for (Item item : items) {
sb.append(item); // ХОРОШО
}
String result = sb.toString();
// 3. ИСПОЛЬЗУЙ intern() только когда необходимо
Set<String> pool = new HashSet<>();
pool.add(s.intern()); // Только если нужна O(1) поиск
// 4. ПАРАМЕТРИЗИРУЙ кодировку
String s = new String(bytes, StandardCharsets.UTF_8);
Итог: При использовании new String():
- Создаётся новый объект в heap, а не берётся из String pool
- Если передан строковый литерал, он добавляется в pool, но новый объект создаётся отдельно
- Это неэффективно для обычных случаев и должно использоваться специально
- String является immutable, поэтому каждая операция создаёт новый объект
- Для конкатенации используй StringBuilder, а не new String