Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
String в Java — полный гайд
String — один из самых часто используемых классов в Java. Понимание его особенностей, иммутабельности и оптимизаций критично для написания эффективного кода.
Иммутабельность (Immutability)
String объекты в Java **неизменяемы**. После создания строку нельзя изменить:
String str = "Hello";
str.concat(" World"); // Это не изменит str!
System.out.println(str); // "Hello"
// concat() возвращает новую строку
String newStr = str.concat(" World");
System.out.println(newStr); // "Hello World"
Почему это важно:
- Потокобезопасность: одну строку могут читать многие потоки одновременно
- Безопасность: строки хранят пароли, токены и не могут быть изменены
- Оптимизация: JVM может кэшировать и переиспользовать строки
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // true! Одна и та же строка в памяти
String s3 = new String("Hello");
System.out.println(s1 == s3); // false! Разные объекты
System.out.println(s1.equals(s3)); // true! Одинаковое содержимое
String Pool (Пул строк)
JVM поддерживает специальную область памяти — String Pool, где хранятся уникальные строковые литералы:
// В String Pool
String literal1 = "Java"; // Добавляется в pool
String literal2 = "Java"; // Берется из pool
System.out.println(literal1 == literal2); // true
// Не в String Pool
String obj1 = new String("Java");
String obj2 = new String("Java");
System.out.println(obj1 == obj2); // false (разные объекты в heap)
// intern() добавляет строку в pool
String obj3 = new String("Java").intern();
System.out.println(literal1 == obj3); // true!
Операции со String
String str = "Hello World";
// Получение длины
int length = str.length(); // 11
// Получение символа по индексу
char ch = str.charAt(0); // H
// Поиск подстроки
int index = str.indexOf("World"); // 6
boolean contains = str.contains("World"); // true
// Подстрока
String sub = str.substring(0, 5); // "Hello"
// Замена
String replaced = str.replace("World", "Java"); // "Hello Java"
// Преобразование регистра
String upper = str.toUpperCase(); // "HELLO WORLD"
String lower = str.toLowerCase(); // "hello world"
// Удаление пробелов
String trimmed = " Hello ".trim(); // "Hello"
// Разделение по разделителю
String[] parts = str.split(" "); // ["Hello", "World"]
// Проверка начала/конца
boolean startsWith = str.startsWith("Hello"); // true
boolean endsWith = str.endsWith("World"); // true
Сравнение строк
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
// == сравнивает ссылки (адреса в памяти)
System.out.println(s1 == s2); // true (String Pool)
System.out.println(s1 == s3); // false (разные объекты)
// equals() сравнивает содержимое
System.out.println(s1.equals(s3)); // true
// equalsIgnoreCase() игнорирует регистр
System.out.println(s1.equalsIgnoreCase("HELLO")); // true
// compareTo() для лексикографического сравнения
System.out.println("ABC".compareTo("ABC")); // 0 (равны)
System.out.println("ABC".compareTo("DEF")); // -3 (ABC < DEF)
StringBuilder vs StringBuffer vs String
// ❌ Неэффективно — много новых объектов в памяти
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // Каждый раз создается новая строка!
}
// ✅ Эффективно — используем StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // Добавляем в одну переменную
}
String result = sb.toString();
Сравнение:
| Класс | Иммутабельный | Потокобезопасный | Скорость |
|---|---|---|---|
| String | Да | Да | Медленно при изменениях |
| StringBuilder | Нет | Нет | Быстро, предпочтительно |
| StringBuffer | Нет | Да | Медленнее, legacy |
String Formatting
// Способ 1: concat и +
String name = "Alice";
String msg = "Hello, " + name + "!";
// Способ 2: format() (как printf в C)
String formatted = String.format("Hello, %s! You are %d years old", "Bob", 25);
// Способ 3: Text Blocks (Java 13+)
String html = """
<html>
<body>Hello</body>
</html>
""";
// Способ 4: String.join()
String joined = String.join(", ", "A", "B", "C"); // "A, B, C"
// Способ 5: StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(", ").append("World");
String result = sb.toString();
Утечки памяти со String
// ❌ Потенциальная утечка памяти
public static void main(String[] args) {
String source = new String("Very large string with lots of data...");
String substring = source.substring(0, 5); // "Very "
// substring удерживает ссылку на весь source в памяти!
// Даже если source больше не нужен, он не будет удален
}
// ✅ Решение в старых версиях Java
String substring = new String(source.substring(0, 5));
// В Java 11+ это уже оптимизировано — substring создает новый массив символов
Строки и Null
String str = null;
// ❌ NullPointerException
System.out.println(str.length()); // NPE!
// ✅ Проверка перед использованием
if (str != null && str.length() > 0) {
System.out.println(str);
}
// ✅ Java 8+: Optional
Optional.ofNullable(str).ifPresent(System.out::println);
// ✅ Java 7+: Objects.requireNonNull()
String safeStr = Objects.requireNonNull(str, "String cannot be null");
Интернирование и производительность
// Плохо: много одинаковых строк в памяти
for (int i = 0; i < 1000000; i++) {
String key = "constant_key"; // Каждый раз создается новый объект
}
// Хорошо: используем String literal
String key = "constant_key"; // Один раз в pool
for (int i = 0; i < 1000000; i++) {
// Переиспользуем одну строку
}
// Или явное интернирование
Set<String> uniqueStrings = new HashSet<>();
for (String str : largeList) {
uniqueStrings.add(str.intern()); // Избегаем дубликатов в памяти
}
Regex с String
String text = "Hello123World456";
// Проверка паттерна
boolean hasNumbers = text.matches(".*\\d+.*"); // true
// Замена по паттерну
String replaced = text.replaceAll("\\d+", "X"); // "HelloXWorldX"
// Разделение по паттерну
String[] parts = text.split("\\d+"); // ["Hello", "World", ""]
// Поиск совпадений
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(matcher.group()); // 123, 456
}
Лучшие практики
// 1. Используйте equals(), не ==
String a = "test";
String b = new String("test");
if (a.equals(b)) { } // Правильно
if (a == b) { } // Неправильно
// 2. Используйте StringBuilder для множественных конкатенаций
StringBuilder sb = new StringBuilder();
for (...) {
sb.append(...); // Хорошо
}
// 3. Избегайте substring() на очень больших строках
// в старых Java версиях
// 4. Кэшируйте результаты intern() если часто используется
private static final String COMMON = "very_common_string".intern();
// 5. Используйте String.valueOf() вместо toString()
int num = 42;
String str = String.valueOf(num); // Может вернуть кэшированное значение