Методы класса String

Доступ и диапазоны

Строка индексируется с нуля по кодовым единицам UTF-16.

String value = "JavaNotes";
 
value.length();          // 9
value.charAt(0);         // 'J'
value.substring(4);      // "Notes"
value.substring(0, 4);   // "Java": endIndex не включается

Неверный индекс приводит к StringIndexOutOfBoundsException. Для Unicode-символов, занимающих два char, используют codePointAt, codePoints и offsetByCodePoints.

Поиск и проверка

String path = "notes/java/core";
 
path.contains("java");       // true
path.startsWith("notes/");   // true
path.endsWith("/core");      // true
path.indexOf('/');           // 5
path.lastIndexOf('/');       // 10

Если подстрока не найдена, indexOf возвращает -1.

Для равенства:

"READY".equals(status);              // не падает, если status == null
"ready".equalsIgnoreCase(status);

Преобразование

String source = "  Java Notes  ";
 
source.trim();                 // удаляет символы <= U+0020 по краям
source.strip();                // ориентируется на Unicode whitespace
source.toLowerCase(Locale.ROOT);
source.replace(" ", "-");      // литеральная замена
source.repeat(2);

Методы не меняют исходную строку.

split, replaceAll и регулярные выражения

split, replaceAll, replaceFirst принимают regex:

String[] parts = "a.b.c".split("\\.");
String digitsRemoved = "id-42".replaceAll("\\d+", "");

Точка в regex означает «любой символ», поэтому её экранируют. Обычный replace(CharSequence, CharSequence) не использует regex и безопаснее для буквальной замены.

По умолчанию split удаляет пустые элементы в конце:

"a,b,".split(",").length;     // 2
"a,b,".split(",", -1).length; // 3

Соединение и форматирование

String csv = String.join(",", List.of("a", "b", "c"));
String message = "id=%d, status=%s".formatted(42, "READY");
String number = String.valueOf(42);

Для большого накопления используется StringBuilder, для потока строк — Collectors.joining.

Проверки современного API

"".isEmpty();       // длина == 0
" \t".isBlank();    // только пробельные символы
"a\nb".lines();     // Stream<String>

Метод lines корректнее ручного split для разных разделителей строк.

Ловушки

  • Забывать, что правая граница substring не включается.
  • Передавать литерал в split/replaceAll, не учитывая regex.
  • Терять завершающие пустые поля при split.
  • Ожидать изменения исходной строки.
  • Путать trim и Unicode-aware strip.
  • Использовать toLowerCase() без подходящей локали.

Вопросы на собеседовании

  1. Что возвращает indexOf, если совпадения нет?
  2. Почему "a.b".split(".") работает не так, как ожидают?
  3. Чем replace отличается от replaceAll?
  4. Включается ли правая граница в substring?
  5. Чем trim отличается от strip?
  6. Как сохранить завершающие пустые части при split?

Связанные заметки

Работа со строковыми данными · Работа с исключениями в Java · Класс Collectors и его методы