Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Внутренняя структура String в Java
String — один из самых используемых классов, и его структура значительно изменилась за историю Java. Важно понимать эти изменения для оптимизации производительности.
История эволюции String (важно!)
Java 8 и ранее:
public final class String {
private final char[] value; // Массив char (16-бит символы)
private final int offset;
private final int count;
private int hash;
}
// Проблема: даже для ASCII текста используются 16 бит на символ
Java 9+ (String Compression):
public final class String {
private final byte[] value; // Массив byte!
private final byte coder; // 0 = Latin1 (1 байт), 1 = UTF-16 (2 байта)
private int hash;
}
Как это работает в Java 9+
String ascii = "Hello"; // coder=0, value={0x48,0x65,0x6C,0x6C,0x6F}
// 5 байт вместо 10
String unicode = "Привет"; // coder=1, value={...UTF-16 encoded...}
// 12 байт (6 символов * 2 байта)
String mixed = "H привет"; // coder=1 (нужен UTF-16 для всей строки)
// 16 байт
Структура в памяти (объект String)
Объект String:
┌─────────────────────────────────────┐
│ Object Header (16 байт) │
│ - Mark Word (8 байт) │
│ - Klass Pointer (4 байт) │
│ - Padding (4 байта на 64-bit JVM) │
├─────────────────────────────────────┤
│ byte[] value (8 байт ссылка) │
│ byte coder (1 байт: 0 или 1) │
│ int hash (4 байта, 0 если не считан)│
│ Padding (3 байта выравнивание) │
├─────────────────────────────────────┤
│ Внутри value[]: │
│ Object Header (16 байт) │
│ Array Length (4 байта) │
│ byte[] данные │
└─────────────────────────────────────┘
Размер String на примерах
// Строка "Hi" (ASCII, Java 9+)
String str = "Hi";
// String объект: 24 байта
// byte[] value: 16 + 2 = 18 байт
// Итого: ~42 байта
// Строка "" (пустая)
String empty = "";
// String объект: 24 байта
// byte[] value: 16 + 0 = 16 байт (пустой массив)
// Итого: ~40 байт
String Pool (Intern)
String str1 = "Hello"; // Новая строка в String Pool
String str2 = "Hello"; // Ссылка на ту же строку из Pool
String str3 = new String("Hello"); // Новая строка в Heap
str1 == str2; // true (одна и та же ссылка)
str1 == str3; // false (разные объекты)
str1.equals(str3); // true (одинаковое содержимое)
str3.intern() == str1; // true (str3 добавлен в Pool)
Immutability и как это работает
public final class String implements CharSequence, Comparable<String>, Serializable {
private final byte[] value; // final - не может быть переделано!
private final byte coder; // final
// ...
}
// Попытка изменить
String str = "Hello";
// str.value[0] = 'J'; // ОШИБКА компиляции - private final
// Вместо этого создаётся новая String
String str2 = str + " World"; // Новая строка
Механизм конкатенации строк
До Java 9:
String result = "Hello" + " " + "World"; // Компилятор → StringBuilder
// Более 20 лет это так работает!
Java 9+ (StringBuilder по умолчанию):
String result = "Hello" + " " + "World";
// Компилируется в:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
result = sb.toString();
String Template (Java 21 Preview):
String name = "World";
String result = "Hello, \{name}!"; // Более эффективно
Hashcode кэширование
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
// Кэшируем хеш при первом запросе
hash = h = isLatin1() ?
StringLatin1.hashCode(value) :
StringUTF16.hashCode(value);
}
return h;
}
// Почему это важно:
String key = "myKey";
int h1 = key.hashCode(); // Вычисляется один раз
int h2 = key.hashCode(); // Просто возвращает кэшированное значение
Производительность String операций
// ❌ МЕДЛЕННО - создаст 1000 строк
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // O(n²) - каждая итерация копирует всю строку
}
// ✅ БЫСТРО - использует StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
Substring (важное изменение)
Java 8: Substring делил массив (offset + count)
String big = "0123456789";
String sub = big.substring(5); // Делил массив, экономил память? НЕТ!
// Реально хранит весь char[] и offset/count
Java 9+: Substring создаёт новый массив
String big = "0123456789";
String sub = big.substring(5); // Создаёт новый byte[] с 5 символами
// Правильно - если big удалён, sub может быть GC
Важные выводы
- String использует byte[] с coder для оптимизации памяти (Java 9+)
- String immutable - изменение создаёт новую строку
- String Pool экономит память для часто используемых строк
- Hashcode кэшируется после первого запроса
- Конкатенация через + автоматически переводится в StringBuilder
- Для циклических конкатенаций всегда используй StringBuilder
- Substring с Java 9 создаёт новый массив (нет утечек памяти)