Класс String в Java: что это и основные методы
Типичная первая ошибка со строками: вызвать s.toUpperCase() и ожидать, что строка изменится. После String s = "java"; s.toUpperCase(); System.out.println(s); на экране по-прежнему java — метод вернул новую строку "JAVA", а результат просто потеряли.
String в Java — это класс java.lang.String, объект которого хранит неизменяемую (immutable) последовательность символов. Любой строковый литерал в кавычках, например "Hello", — объект String. Методы класса (equals(), substring(), replace(), toUpperCase() и др.) никогда не меняют исходную строку, а возвращают новую.
Для работы со строками в пакете java.lang, который подключается автоматически, есть три класса: String, StringBuilder и StringBuffer. Все они объявлены как final, поэтому унаследоваться от них нельзя. В этом уроке разбираем класс String; изменяемым строкам посвящён урок StringBuilder и StringBuffer.
Неизменяемость String
Строка в Java неизменяема: после создания объекта String его символы поменять нельзя. Методы, которые «изменяют» строку, создают и возвращают новый объект, поэтому их результат нужно присвоить переменной: s = s.toUpperCase();.
public class ImmutableExample {
public static void main(String[] args) {
String s = "java";
s.toUpperCase(); // результат потерян
System.out.println(s); // Вывод: java
s = s.toUpperCase(); // переменная теперь ссылается на новую строку
System.out.println(s); // Вывод: JAVA
}
} Именно поэтому у String нет методов вроде setCharAt(), append() или reverse(): они есть у изменяемых классов StringBuilder и StringBuffer — подробнее в уроке StringBuilder и StringBuffer.
Запомните
Неизменяем объект, а не переменная. Строку "java" поменять нельзя, но переменную s можно перенаправить на другой объект. Если строку нужно много раз менять в цикле, используйте StringBuilder: каждое s = s + x создаёт новый объект String.
Основные методы класса String
Чаще всего в Java используют методы String для сравнения (equals(), compareTo()), поиска (indexOf(), contains()), извлечения частей (charAt(), substring()) и преобразования (replace(), toUpperCase(), trim()). Сводка с примерами и граничными случаями:
| Метод | Что делает | Пример → результат | Граничный случай |
|---|---|---|---|
length() | Длина строки (число char) | "abc".length() → 3 | "".length() → 0 |
charAt(i) | Символ по индексу (с 0) | "abc".charAt(1) → 'b' | Индекс вне диапазона → StringIndexOutOfBoundsException |
equals(s) | Сравнивает содержимое | "Hi".equals("Hi") → true | Вызов на null → NullPointerException |
equalsIgnoreCase(s) | Сравнение без учёта регистра | "Hi".equalsIgnoreCase("HI") → true | Аргумент null → false |
compareTo(s) | Лексикографическое сравнение | "a".compareTo("b") → -1 | Заглавные буквы «меньше» строчных |
indexOf(x) | Индекс первого вхождения | "hello".indexOf('l') → 2 | Не найдено → -1 |
lastIndexOf(x) | Индекс последнего вхождения | "hello".lastIndexOf('l') → 3 | Не найдено → -1 |
contains(s) | Есть ли подстрока | "Foobar".contains("oo") → true | contains("") → true |
startsWith(s) / endsWith(s) | Проверка начала / конца | "Foobar".endsWith("bar") → true | Регистр учитывается |
substring(b, e) | Подстрока с b по e-1 | "Hello".substring(1, 3) → "el" | b > e → StringIndexOutOfBoundsException |
replace(a, b) | Заменяет все вхождения символа или подстроки | "Hello".replace('l', 'w') → "Hewwo" | Не регулярное выражение (для regex — replaceAll()) |
toLowerCase() / toUpperCase() | Смена регистра | "Hello".toUpperCase() → "HELLO" | Зависит от локали по умолчанию |
trim() / strip() | Удаляет пробелы по краям | " Hi ".trim() → "Hi" | strip() (Java 11) понимает Unicode-пробелы |
isEmpty() / isBlank() | Пустая / пустая или из пробелов | " ".isBlank() → true | " ".isEmpty() → false |
concat(s) | Присоединяет строку | "Hello".concat("!") → "Hello!" | Аргумент null → NullPointerException |
String.join(d, ...) | Склеивает через разделитель (Java 8) | String.join("-", "a", "b") → "a-b" | Элемент null превращается в "null" |
String.valueOf(x) | Любое значение → строка | String.valueOf(42) → "42" | (Object) null → "null" |
repeat(n) | Повторяет строку n раз (Java 11) | "ab".repeat(3) → "ababab" | n < 0 → IllegalArgumentException |
toCharArray() | Копия символов в массив char[] | "abc".toCharArray() → [a, b, c] | Изменение массива не меняет строку |
split(regex) | Разбивает строку по шаблону | "a,b".split(",") → [a, b] | Аргумент — регулярное выражение: split(".") вернёт пустой массив |
Метод split() принимает регулярное выражение — он подробно разобран в уроке Регулярные выражения, а форматирование через String.format() — в уроке Форматирование строк.
Создание строк и длина строки
Строку в Java создают литералом (String s = "abc";) или конструктором (new String(...)) из массива символов, массива байтов или другой строки. Длину строки возвращает метод length() — это метод, а не поле, как у массива.
Класс String поддерживает несколько конструкторов, например:
String()— пустая строка;String(String original)— копия другой строки;String(char[] value)иString(char[] value, int offset, int count)— из массива символов;String(byte[] bytes)иString(byte[] bytes, Charset charset)— из массива байтов;String(StringBuffer buffer),String(StringBuilder builder)и др.
Рассмотрим разные варианты создания строк:
public class StringExample1 {
public static void main(String[] args) {
String str1 = new String();
System.out.println("str1:[" + str1 + "]");
char[] chars1 = {'a', 'b', 'c'};
String str2 = new String(chars1);
System.out.println("str2:" + str2);
char[] chars2 = {'a', 'b', 'c', 'd', 'e', 'f'};
String str3 = new String(chars2, 2, 3);
System.out.println("str3:" + str3);
String str4 = "abc";
System.out.println("str4:" + str4);
System.out.println("str4.length:" + str4.length());
System.out.println("abc.length:" + "abc".length());
String str5 = new String(str4);
System.out.println("str5:" + str5);
}
} str1:[]
str2:abc
str3:cde
str4:abc
str4.length:3
abc.length:3
str5:abc В следующем примере строки создаются из массива типа byte. Конструктор String(byte[]) декодирует байты кодировкой по умолчанию (начиная с Java 18 это UTF-8); если кодировка известна, лучше передать её явно: new String(bytes, StandardCharsets.UTF_8).
public class StringExample2 {
public static void main(String[] args) {
byte[] bytes = {65, 66, 67, 68, 69, 70};
String s1 = new String(bytes);
System.out.println(s1); // Вывод: ABCDEF
String s2 = new String(bytes, 2, 3);
System.out.println(s2); // Вывод: CDE
}
} Сравнение строк: equals(), equalsIgnoreCase() и ==
Содержимое строк в Java сравнивают методом equals(), а без учёта регистра — методом equalsIgnoreCase(). Оператор == сравнивает не текст, а ссылки: он показывает, указывают ли две переменные на один и тот же объект.
public class EqualsExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Good-bye";
String s4 = "HELLO";
System.out.println(s1 + " equals " + s2 + " -> "
+ s1.equals(s2));
System.out.println(s1 + " equals " + s3 + " -> "
+ s1.equals(s3));
System.out.println(s1 + " equals " + s4 + " -> "
+ s1.equals(s4));
System.out.println(s1 + " equalsIgnoreCase " + s4 + " -> "
+ s1.equalsIgnoreCase(s4));
}
} Hello equals Hello -> true
Hello equals Good-bye -> false
Hello equals HELLO -> false
Hello equalsIgnoreCase HELLO -> true В Java есть String Pool — область памяти (с Java 7 — в куче), где хранятся строковые литералы. Встретив литерал, например "Java", JVM сначала ищет такую строку в пуле и, если она уже есть, возвращает ссылку на существующий объект вместо создания нового.
Конструктор пул не использует: new String("Java") всегда создаёт новый объект в куче.

Следующий пример демонстрирует сравнение строк с помощью == и метода equals():
public class EqualStrings {
public static void main(String[] args) {
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
String s4 = new String(s1);
System.out.println("s1==s2 : " + (s1 == s2));
System.out.println("s3==s4 : " + (s3 == s4));
System.out.println("s1==s3 : " + (s1 == s3));
System.out.println("s1 equals s2 : " + s1.equals(s2));
System.out.println("s1 equals s3 : " + s1.equals(s3));
}
} s1==s2 : true
s3==s4 : false
s1==s3 : false
s1 equals s2 : true
s1 equals s3 : true Что выведет этот код? В пул автоматически попадают только литералы и константы времени компиляции. Строка, склеенная во время выполнения, — это новый объект, даже если её текст совпадает с литералом:
public class StringPoolQuiz {
public static void main(String[] args) {
String a = "Java";
String part = "Ja";
String b = part + "va"; // склейка во время выполнения
final String constPart = "Ja";
String c = constPart + "va"; // константа времени компиляции
System.out.println(a == b); // Вывод: false
System.out.println(a == c); // Вывод: true
System.out.println(a == b.intern()); // Вывод: true
System.out.println(a.equals(b)); // Вывод: true
}
} Метод intern() возвращает строку из пула с тем же содержимым (и помещает её туда, если её там ещё нет).
Важно
Не сравнивайте строки через ==. Код может «работать» на литералах и ломаться на строках, прочитанных из файла, введённых с клавиатуры или собранных во время выполнения. Для сравнения текста всегда используйте equals().
Сравнение подстрок: startsWith(), endsWith(), contains()
Чтобы проверить часть строки, в классе String есть методы startsWith(), endsWith(), contains() и regionMatches(). Все они возвращают boolean и учитывают регистр символов.
- Метод startsWith() проверяет, начинается ли строка с префикса, переданного в параметре.
- Метод endsWith() проверяет, заканчивается ли строка переданным суффиксом.
- Метод contains() проверяет, входит ли подстрока в строку.
- Метод regionMatches() сравнивает фрагмент исходной строки с фрагментом в строке-параметре.
Рассмотрим пример:
public class StringExample5 {
public static void main(String[] args) {
System.out.println("Foobar".endsWith("bar")); // Вывод: true
System.out.println("Foobar".startsWith("Foo")); // Вывод: true
System.out.println("Foobar".contains("oo")); // Вывод: true
// 3 символа с индекса 6 строки ("wor") против 3 символов "world" с индекса 0 ("wor")
System.out.println("Hello world!!!".regionMatches(6, "world", 0, 3)); // Вывод: true
System.out.println("Foobar".startsWith("foo")); // Вывод: false (регистр)
}
} Метод compareTo()
Метод compareTo(str) сравнивает строки лексикографически (посимвольно по кодам Unicode) и возвращает число: меньше нуля — вызывающая строка меньше str, больше нуля — больше, ноль — строки равны. Он нужен для сортировки, где мало знать, равны ли строки.
Возвращается разность кодов первых несовпадающих символов, а если одна строка — начало другой, то разность длин:
System.out.println("a".compareTo("b")); // Вывод: -1
System.out.println("apple".compareTo("app")); // Вывод: 2
System.out.println("Java".compareTo("java")); // Вывод: -32 ('J' = 74, 'j' = 106)
System.out.println("Java".compareToIgnoreCase("java")); // Вывод: 0 Следующий пример сортирует массив строк простой сортировкой обменом на основе compareTo(). В реальном коде так же сработает одна строка Arrays.sort(array).
import java.util.Arrays;
public class SortString {
private static String[] array = {
"Для", "работы", "со", "строками", "в", "языке",
"используются", "классы", "Java", "String", "StringBuilder", "StringBuffer"
};
public static void main(String[] args) {
for (int j = 0; j < array.length; j++) {
for (int i = j + 1; i < array.length; i++) {
if (array[i].compareTo(array[j]) < 0) {
String t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
System.out.println(Arrays.toString(array));
}
} [Java, String, StringBuffer, StringBuilder, Для, в, используются, классы, работы, со, строками, языке] Обратите внимание на порядок: латинские буквы идут раньше кириллицы, а заглавные — раньше строчных, поэтому «Для» оказалось перед «в». Так получается потому, что compareTo() сравнивает коды символов, а не алфавит конкретного языка. Для сортировки «по-человечески» используют compareToIgnoreCase() или java.text.Collator.
Извлечение символов: метод charAt()
Метод charAt(index) возвращает символ (char) строки по индексу; нумерация начинается с 0, последний символ имеет индекс length() - 1. При индексе вне этого диапазона выбрасывается StringIndexOutOfBoundsException.
public class StringExample3 {
public static void main(String[] args) {
char ch = "abc".charAt(1);
System.out.println(ch); // Вывод: b
System.out.println("abc".charAt(3)); // StringIndexOutOfBoundsException
}
} Поиск подстроки или символа: indexOf(), lastIndexOf()
Для поиска символа или подстроки в строке Java используются два метода: indexOf() ищет первое вхождение, lastIndexOf() — последнее. Оба возвращают индекс найденного символа или начала подстроки, а если ничего не найдено, — -1.
Чтобы найти первое или последнее вхождение символа, применяйте:
int indexOf(int ch)
int lastIndexOf(int ch) Здесь ch — символ, который нужно найти (тип int, поэтому можно передать char). Чтобы найти первое или последнее вхождение подстроки, применяйте:
int indexOf(String str)
int lastIndexOf(String str) Начальную позицию поиска можно указать вторым параметром. lastIndexOf() при этом ищет назад от startIndex:
int indexOf(int ch, int startIndex)
int lastIndexOf(int ch, int startIndex)
int indexOf(String str, int startIndex)
int lastIndexOf(String str, int startIndex) Рассмотрим применение этих методов на следующем примере:
public class IndexOfExample {
public static void main(String[] args) {
String s = "Для работы со строками в языке Java используются "
+ "классы String, StringBuilder, StringBuffer.";
System.out.println(s);
System.out.println("indexOf(S) = "
+ s.indexOf('S'));
System.out.println("lastIndexOf(r) = "
+ s.lastIndexOf('r'));
System.out.println("indexOf(String) = "
+ s.indexOf("String"));
System.out.println("lastIndexOf(String) = "
+ s.lastIndexOf("String"));
System.out.println("indexOf(S, 60) = "
+ s.indexOf('S', 60));
System.out.println("lastIndexOf(S, 70) = "
+ s.lastIndexOf('S', 70));
System.out.println("indexOf(Kotlin) = "
+ s.indexOf("Kotlin"));
}
} Для работы со строками в языке Java используются классы String, StringBuilder, StringBuffer.
indexOf(S) = 56
lastIndexOf(r) = 90
indexOf(String) = 56
lastIndexOf(String) = 79
indexOf(S, 60) = 64
lastIndexOf(S, 70) = 64
indexOf(Kotlin) = -1 Если нужен только ответ «есть или нет», проще вызвать contains(): s.contains("Java") читается лучше, чем s.indexOf("Java") != -1.
Методы concat(), replace(), toLowerCase(), toUpperCase(), trim()
Эти методы возвращают изменённую копию строки: concat() присоединяет строку в конец, replace() заменяет все вхождения символа или подстроки, toLowerCase() и toUpperCase() переводят строку в нижний или верхний регистр, trim() удаляет пробелы в начале и в конце.
public class ModifyStringExample {
public static void main(String[] args) {
System.out.println("Hello".concat(" World")); // Вывод: Hello World
System.out.println("Hello".replace('l', 'w')); // Вывод: Hewwo
System.out.println("Hello".replace("ll", "LL")); // Вывод: HeLLo
System.out.println("Hello".toLowerCase()); // Вывод: hello
System.out.println("Hello".toUpperCase()); // Вывод: HELLO
System.out.println("[" + " Hello World ".trim() + "]"); // Вывод: [Hello World]
System.out.println("[" + " Hello World ".strip() + "]"); // Вывод: [Hello World]
}
} replace() не работает с регулярными выражениями — для замены по шаблону есть replaceAll() (см. урок Регулярные выражения). Метод strip() (Java 11) похож на trim(), но удаляет и Unicode-пробелы, а trim() — только символы с кодом до U+0020 (пробел).
Неочевидный момент
toLowerCase() и toUpperCase() без аргументов используют локаль по умолчанию. В турецкой локали "TITLE".toLowerCase() вернёт "tıtle" (с i без точки), и сравнение с "title" не пройдёт. Для ключей, команд и идентификаторов пишите toLowerCase(Locale.ROOT).
Преобразование данных: valueOf() и parseInt()
Статический метод String.valueOf() превращает в строку значение любого типа: примитив, массив символов или объект. Обратное преобразование — парсинг строки в число — выполняют методы классов-обёрток: Integer.parseInt(), Long.parseLong(), Double.parseDouble().
Перегруженные версии valueOf() есть для всех типов, например:
static String valueOf(int i)
static String valueOf(double d)
static String valueOf(boolean b)
static String valueOf(Object obj)
static String valueOf(char[] data) public class ValueOfExample {
public static void main(String[] args) {
System.out.println(String.valueOf(42) + 1); // Вывод: 421 (строка + число)
System.out.println(String.valueOf(3.5)); // Вывод: 3.5
System.out.println(String.valueOf(true)); // Вывод: true
Object obj = null;
System.out.println(String.valueOf(obj)); // Вывод: null
int n = Integer.parseInt("42");
System.out.println(n + 1); // Вывод: 43
double d = Double.parseDouble("2.5");
System.out.println(d * 2); // Вывод: 5.0
Integer.parseInt("42abc"); // NumberFormatException
}
} В отличие от obj.toString(), вызов String.valueOf(obj) безопасен для null: он вернёт строку "null", а не выбросит NullPointerException.
Метод substring()
Метод substring() возвращает часть строки. У него два варианта: substring(beginIndex) — от указанной позиции до конца и substring(beginIndex, endIndex) — от beginIndex включительно до endIndex не включительно. Длина результата равна endIndex - beginIndex.
public class SubstringExample {
public static void main(String[] args) {
System.out.println("Welcome to Kharkiv!!!".substring(11)); // Вывод: Kharkiv!!!
System.out.println("Welcome to Kharkiv!!!".substring(11, 18)); // Вывод: Kharkiv
System.out.println("Welcome".substring(7)); // Вывод: (пустая строка)
System.out.println("Welcome".substring(8)); // StringIndexOutOfBoundsException
}
} substring(length()) — допустимый вызов, он возвращает пустую строку; исключение возникает только при индексе больше длины строки, отрицательном индексе или beginIndex > endIndex.
Сложение строк: +, concat() и join()
Сложить (конкатенировать) строки в Java можно оператором +, методом concat() или статическим методом String.join(), который появился в JDK 8 и склеивает несколько строк через разделитель. Оператор + удобнее всего: он сам превращает числа, null и объекты в строку.
import java.util.List;
public class StringJoinExample {
public static void main(String[] args) {
System.out.println("1" + 2 + 3); // Вывод: 123
System.out.println(1 + 2 + "3"); // Вывод: 33
String s = null;
System.out.println("Значение: " + s); // Вывод: Значение: null
// "Значение: ".concat(s); // NullPointerException
String result = String.join(":", "foobar", "foo", "bar");
System.out.println(result); // Вывод: foobar:foo:bar
System.out.println(String.join(", ", List.of("a", "b", "c"))); // Вывод: a, b, c
}
} Выражение вычисляется слева направо: в "1" + 2 + 3 сначала получается строка "12", к которой приклеивается 3, а в 1 + 2 + "3" сначала складываются числа. Для многократного сложения в цикле используйте StringBuilder (см. урок StringBuilder и StringBuffer), для форматированной склейки — String.format().
Методы isEmpty() и isBlank()
Метод isEmpty() проверяет, пуста ли строка: он возвращает true, только если её длина равна 0. Метод isBlank() (Java 11) возвращает true, если строка пуста или состоит только из пробельных символов (пробелы, табуляции, переводы строк).
public class StringEmpty {
public static void main(String[] args) {
System.out.println("".isEmpty()); // Вывод: true
System.out.println(" ".isEmpty()); // Вывод: false
System.out.println(" ".isBlank()); // Вывод: true
System.out.println("\t\n".isBlank()); // Вывод: true
String s = null;
System.out.println(s.isEmpty()); // NullPointerException
}
} Ни один из методов не проверяет null, поэтому безопасная проверка «строка не заполнена» выглядит так: s == null || s.isBlank().
Часто задаваемые вопросы
Почему String в Java сделан неизменяемым?
Неизменяемость позволяет безопасно делить одну строку между многими ссылками через String Pool, кэшировать hashCode (поэтому строки хорошо работают ключами HashMap), передавать строки между потоками без синхронизации и не бояться, что имя файла или URL изменят после проверки прав. Как это выглядит в коде — в разделе Неизменяемость String.
Как сравнить строки, если одна из них может быть null?
Используйте Objects.equals(a, b) из java.util — он возвращает true для двух null и не выбрасывает исключение. Если одна сторона — литерал, ставьте его первым: "yes".equals(answer). Разница между == и equals() разобрана в разделе Сравнение строк.
Сколько объектов создаёт new String("Java")?
До двух: литерал "Java" попадает в String Pool (если его там ещё не было), а new всегда создаёт ещё один объект в куче. Если литерал уже есть в пуле, создаётся только один новый объект. Поэтому new String("...") на практике почти не нужен.
Чем length() у строки отличается от length у массива?
У строки длина — метод s.length(), у массива — поле arr.length без скобок. Кроме того, length() считает не буквы, а 16-битные значения char: символ эмодзи занимает два char, поэтому "😀".length() равно 2. Число «настоящих» символов даёт s.codePointCount(0, s.length()).
Как в Java перевести строку в число?
Методами Integer.parseInt("42"), Long.parseLong(), Double.parseDouble(). Если строка не является числом (например, "42abc" или пустая строка), выбрасывается NumberFormatException; пробелы по краям тоже недопустимы, поэтому ввод пользователя сначала обрезают через strip(). Пример — в разделе valueOf() и parseInt().
Practice
Reinforce this lesson with hands-on tasks, each with a level and a time estimate.
- Практика методов класса String
- Заменить каждое второе вхождение строки
- Найти два средних знака строки
- Слово с минимальным числом символов
- Найти количество слов
Video Explanation
Prefer video format? Watch this lesson with examples and explanations.
Comments