☕ Java

Методы строк — полное руководство в Java: подробное руководство

Что это такое и зачем? (аналогия из реальной жизни)

Представь, что ты работаешь в библиотеке. У тебя есть книга — это строка текста. Но книга может быть в разных состояниях: в твёрдой обложке, с закладками, с пометками на полях. Ты можешь с книгой делать разные вещи: перелистывать страницы, искать определённое слово, заменять старые страницы на новые, вырезать кусок текста или склеивать две книги вместе.

В программировании строка (String) — это та же книга. Это последовательность символов: букв, цифр, пробелов, знаков препинания. А методы строк — это те самые действия, которые ты можешь с этой «книгой» совершать. Методы — это встроенные функции, которые уже написаны за тебя. Тебе не нужно придумывать, как найти слово в тексте или как сделать все буквы заглавными — Java уже всё умеет.

💡 Важно понять: строки в Java — это объекты. У каждого объекта есть методы. Когда ты пишешь точку после имени строки, ты говоришь: «Эй, строка, сделай что-то со своим содержимым!»

Зачем это нужно? Представь, что ты пишешь программу для проверки пароля. Тебе нужно узнать, длиннее ли он 8 символов, есть ли в нём цифры, не совпадает ли он с именем пользователя. Без методов строк тебе пришлось бы писать десятки строк кода, перебирая каждый символ вручную. С методами — это одна-две команды.

Теория: как это работает изнутри

Давай заглянем под капот. Когда ты пишешь String text = "Привет";, Java делает несколько важных вещей:

  1. Создаёт объект класса String в специальной области памяти — куче (heap).
  2. Запоминает массив символов внутри этого объекта. Строка — это, по сути, массив типа char[]. Каждая буква, пробел или знак — отдельный элемент этого массива.
  3. Фиксирует длину — количество символов в массиве.

Самая важная особенность строк в Java — они неизменяемые (immutable). Это значит, что когда ты вызываешь метод, который «меняет» строку, на самом деле создаётся новая строка. Старая остаётся нетронутой.

⚠️ Запомни раз и навсегда: String — неизменяемый тип. Любой метод, который выглядит как «изменение» строки (замена, удаление, перевод в верхний регистр), возвращает новую строку. Оригинал не меняется никогда.

Почему так сделано? Во-первых, это безопасно. Если ты передал строку в метод, ты уверен, что метод её не испортит. Во-вторых, это позволяет Java оптимизировать память: одинаковые строки могут храниться в одном месте (это называется пул строк).

Внутри класса String есть около 70 методов. Мы разберём самые важные, которые ты будешь использовать в 99% случаев.

Синтаксис в Java: все формы и варианты

Методы строк вызываются через точку. Общий синтаксис:

результат = строка.имяМетода(аргументы);

Где результат — это то, что метод возвращает (число, новую строку, логическое значение), а аргументы — данные, которые ты передаёшь методу (например, символ для поиска или новую подстроку).

Вот полная таблица самых популярных методов. Держи её под рукой — она пригодится:

Метод Что делает Что возвращает Пример
length() Считает количество символов int (целое число) "Кот".length() → 3
charAt(int index) Возвращает символ на указанной позиции char (один символ) "Кот".charAt(1) → 'о'
toUpperCase() Переводит все буквы в верхний регистр String (новая строка) "кот".toUpperCase() → "КОТ"
toLowerCase() Переводит все буквы в нижний регистр String (новая строка) "Кот".toLowerCase() → "кот"
equals(Object obj) Сравнивает строки по содержимому boolean (true/false) "кот".equals("кот") → true
equalsIgnoreCase(String str) Сравнивает строки без учёта регистра boolean "Кот".equalsIgnoreCase("кот") → true
contains(CharSequence s) Проверяет, содержит ли строка подстроку boolean "Привет".contains("рив") → true
startsWith(String prefix) Проверяет, начинается ли строка с подстроки boolean "Java".startsWith("Ja") → true
endsWith(String suffix) Проверяет, заканчивается ли строка на подстроку boolean "Java".endsWith("va") → true
indexOf(String str) Ищет первое вхождение подстроки int (индекс или -1) "Кот".indexOf("т") → 2
lastIndexOf(String str) Ищет последнее вхождение подстроки int (индекс или -1) "Кот".lastIndexOf("т") → 2
substring(int beginIndex) Вырезает часть строки от индекса до конца String (новая строка) "Привет".substring(3) → "вет"
substring(int begin, int end) Вырезает часть строки от begin до end (не включая end) String "Привет".substring(0, 3) → "При"
replace(char old, char new) Заменяет все вхождения одного символа на другой String "Кот".replace('т', 'д') → "Код"
replace(CharSequence target, CharSequence replacement) Заменяет одну подстроку на другую String "Кот".replace("Ко", "Ро") → "Рот"
trim() Удаляет пробелы в начале и конце строки String " Кот ".trim() → "Кот"
split(String regex) Разбивает строку на массив по разделителю String[] (массив строк) "Кот,Пёс".split(",") → ["Кот", "Пёс"]
isEmpty() Проверяет, пустая ли строка (длина 0) boolean "".isEmpty() → true
isBlank() Проверяет, пустая ли строка или состоит только из пробелов boolean " ".isBlank() → true
toCharArray() Превращает строку в массив символов char[] "Кот".toCharArray() → ['К', 'о', 'т']
concat(String str) Соединяет две строки (как +) String "Ко".concat("т") → "Кот"
repeat(int count) Повторяет строку указанное количество раз String "Ко".repeat(2) → "КоКо"
🔑 Ключевой момент: Индексы в строках начинаются с 0. Первый символ — это индекс 0, второй — 1, и так далее. Последний символ имеет индекс length() - 1.

Примеры кода — от простого к сложному

Пример 1: Знакомство с длиной и символами

public class StringExample1 {
    public static void main(String[] args) {
        // Создаём строку
        String greeting = "Привет, мир!";
        
        // Узнаём длину строки
        int length = greeting.length();
        System.out.println("Длина строки: " + length); // Выведет: 13
        
        // Получаем символ на позиции 0 (первый символ)
        char firstChar = greeting.charAt(0);
        System.out.println("Первый символ: " + firstChar); // Выведет: П
        
        // Получаем символ на позиции 7 (восьмой символ)
        char eighthChar = greeting.charAt(7);
        System.out.println("Символ на позиции 7: " + eighthChar); // Выведет: м
        
        // Важно: если запросить индекс за пределами строки, будет ошибка
        // char error = greeting.charAt(100); // ❌ Ошибка! StringIndexOutOfBoundsException
    }
}

Пример 2: Регистр букв и сравнение

public class StringExample2 {
    public static void main(String[] args) {
        String original = "Java Программирование";
        
        // Переводим в верхний регистр
        String upper = original.toUpperCase();
        System.out.println("Верхний регистр: " + upper); // JAVA ПРОГРАММИРОВАНИЕ
        
        // Переводим в нижний регистр
        String lower = original.toLowerCase();
        System.out.println("Нижний регистр: " + lower); // java программирование
        
        // Сравниваем строки
        String password1 = "Secret123";
        String password2 = "secret123";
        
        // equals() — строгое сравнение (регистр важен)
        boolean isEqual = password1.equals(password2);
        System.out.println("Строгое сравнение: " + isEqual); // false
        
        // equalsIgnoreCase() — сравнение без учёта регистра
        boolean isEqualIgnoreCase = password1.equalsIgnoreCase(password2);
        System.out.println("Сравнение без регистра: " + isEqualIgnoreCase); // true
        
        // Сравнение через == (НЕ ДЕЛАЙ ТАК!)
        String a = "Привет";
        String b = "Привет";
        String c = new String("Привет");
        
        System.out.println(a == b);      // true (но это случайность, из-за пула строк)
        System.out.println(a == c);      // false (разные объекты в памяти)
        System.out.println(a.equals(c)); // true (правильное сравнение)
    }
}
⚠️ Ошибка новичков: Никогда не используй == для сравнения строк! == сравнивает ссылки (адреса в памяти), а не содержимое. Всегда используй equals().

Пример 3: Поиск подстрок и проверка содержимого

public class StringExample3 {
    public static void main(String[] args) {
        String text = "Я люблю программировать на Java";
        
        // Проверяем, содержит ли строка подстроку
        boolean containsJava = text.contains("Java");
        System.out.println("Содержит 'Java': " + containsJava); // true
        
        // Проверяем начало и конец строки
        boolean startsWith = text.startsWith("Я люблю");
        System.out.println("Начинается с 'Я люблю': " + startsWith); // true
        
        boolean endsWith = text.endsWith("Java");
        System.out.println("Заканчивается на 'Java': " + endsWith); // true
        
        // Ищем позицию подстроки
        int indexOfJava = text.indexOf("Java");
        System.out.println("Позиция 'Java': " + indexOfJava); // 28
        
        // Если подстрока не найдена, возвращается -1
        int indexOfPython = text.indexOf("Python");
        System.out.println("Позиция 'Python': " + indexOfPython); // -1
        
        // Ищем последнее вхождение
        String repeated = "кот кот кот";
        int lastIndex = repeated.lastIndexOf("кот");
        System.out.println("Последнее 'кот' на позиции: " + lastIndex); // 8
    }
}

Пример 4: Извлечение частей строки

public class StringExample4 {
    public static void main(String[] args) {
        String fullName = "Иванов Иван Иванович";
        
        // Извлекаем фамилию (первые 6 символов)
        String lastName = fullName.substring(0, 6);
        System.out.println("Фамилия: " + lastName); // Иванов
        
        // Извлекаем имя (с 7-го символа до конца)
        // Сначала найдём пробел после фамилии
        int firstSpace = fullName.indexOf(' ');
        int secondSpace = fullName.indexOf(' ', firstSpace + 1);
        
        String firstName = fullName.substring(firstSpace + 1, secondSpace);
        System.out.println("Имя: " + firstName); // Иван
        
        // Извлекаем отчество (от второго пробела до конца)
        String patronymic = fullName.substring(secondSpace + 1);
        System.out.println("Отчество: " + patronymic); // Иванович
        
        // Практический пример: извлекаем домен из email
        String email = "user@gmail.com";
        int atIndex = email.indexOf('@');
        String domain = email.substring(atIndex + 1);
        System.out.println("Домен: " + domain); // gmail.com
    }
}

Пример 5: Замена и очистка строк

public class StringExample5 {
    public static void main(String[] args) {
        // Замена символов
        String dirty = "Грязь!";
        String clean = dirty.replace('!', '.');
        System.out.println("После замены: " + clean); // Грязь.
        
        // Замена подстрок
        String phrase = "Я люблю Python";
        String newPhrase = phrase.replace("Python", "Java");
        System.out.println("Новая фраза: " + newPhrase); // Я люблю Java
        
        // Удаление пробелов в начале и конце
        String withSpaces = "   Привет, мир!   ";
        String trimmed = withSpaces.trim();
        System.out.println("Без пробелов: '" + trimmed + "'"); // 'Привет, мир!'
        
        // Проверка на пустоту
        String empty = "";
        String blank = "   ";
        
        System.out.println("empty.isEmpty(): " + empty.isEmpty());   // true
        System.out.println("blank.isEmpty(): " + blank.isEmpty());   // false (есть пробелы)
        System.out.println("blank.isBlank(): " + blank.isBlank());   // true (только пробелы)
        
        // Повторение строки
        String laugh = "Ха".repeat(3);
        System.out.println("Смех: " + laugh); // ХаХаХа
    }
}

Пример 6: Разбиение строк и работа с массивами

public class StringExample6 {
    public static void main(String[] args) {
        // Разбиваем строку по запятой
        String csv = "яблоко,груша,банан,апельсин";
        String[] fruits = csv.split(",");
        
        System.out.println("Список фруктов:");
        for (int i = 0; i < fruits.length; i++) {
            System.out.println((i + 1) + ". " + fruits[i]);
        }
        // Выведет:
        // 1. яблоко
        // 2. груша
        // 3. банан
        // 4. апельсин
        
        // Превращаем строку в массив символов
        String word = "Java";
        char[] letters = word.toCharArray();
        
        System.out.print("Буквы слова Java: ");
        for (char letter : letters) {
            System.out.print(letter + " "); // J a v a
        }
        System.out.println();
        
        // Соединяем строки (конкатенация)
        String part1 = "Hello";
        String part2 = "World";
        String result = part1.concat(", ").concat(part2).concat("!");
        System.out.println(result); // Hello, World!
        
        // То же самое через +
        String result2 = part1 + ", " + part2 + "!";
        System.out.println(result2); // Hello, World!
    }
}

Пример 7: Сложный пример — валидация пароля

public class StringExample7 {
    public static void main(String[] args) {
        // Проверяем, соответствует ли пароль требованиям безопасности
        String password = "MyP@ssw0rd";
        
        // 1. Длина пароля должна быть не менее 8 символов
        boolean isLongEnough = password.length() >= 8;
        System.out.println("Длина >= 8: " + isLongEnough); // true
        
        // 2. Пароль должен содержать хотя бы одну цифру
        boolean hasDigit = false;
        for (int i = 0; i < password.length(); i++) {
            if (Character.isDigit(password.charAt(i))) {
                hasDigit = true;
                break;
            }
        }
        System.out.println("Есть цифра: " + hasDigit); // true
        
        // 3. Пароль должен содержать хотя бы одну заглавную букву
        boolean hasUpperCase = false;
        for (int i = 0; i < password.length(); i++) {
            if (Character.isUpperCase(password.charAt(i))) {
                hasUpperCase = true;
                break;
            }
        }
        System.out.println("Есть заглавная: " + hasUpperCase); // true
        
        // 4. Пароль не должен содержать пробелов
        boolean noSpaces = !password.contains(" ");
        System.out.println("Нет пробелов: " + noSpaces); // true
        
        // 5. Проверяем, не совпадает ли пароль с именем пользователя
        String username = "user123";
        boolean notSameAsUsername = !password.equalsIgnoreCase(username);
        System.out.println("Не совпадает с именем: " + notSameAsUsername); // true
        
        // Итоговая проверка
        if (isLongEnough && hasDigit && hasUpperCase && noSpaces && notSameAsUsername) {
            System.out.println("✅ Пароль надёжный!");
        } else {
            System.out.println("❌ Пароль не соответствует требованиям");
        }
    }
}

Пример 8: Работа с форматированием (продвинутый уровень)

public class StringExample8 {
    public static void main(String[] args) {
        // Форматирование строк (как в Python format)
        String name = "Анна";
        int age = 25;
        double salary = 75000.50;
        
        // Используем String.format()
        String formatted = String.format("Имя: %s, Возраст: %d, Зарплата: %.2f", name, age, salary);
        System.out.println(formatted); // Имя: Анна, Возраст: 25, Зарплата: 75000.50
        
        // Спецификаторы формата:
        // %s — строка
        // %d — целое число
        // %f — число с плавающей точкой
        // %.2f — число с двумя знаками после запятой
        // %n — перевод строки
        
        // Сравнение строк с игнорированием пробелов
        String str1 = "  Java  ";
        String str2 = "java";
        
        // Сначала trim(), потом equalsIgnoreCase()
        boolean areEqual = str1.trim().equalsIgnoreCase(str2);
        System.out.println("Строки равны (без учёта пробелов и регистра): " + areEqual); // true
        
        // Проверка, состоит ли строка только из букв
        String onlyLetters = "Привет";
        String withNumbers = "Привет123";
        
        boolean isOnlyLetters1 = onlyLetters.chars().allMatch(Character::isLetter);
        boolean isOnlyLetters2 = withNumbers.chars().allMatch(Character::isLetter);
        
        System.out.println("Только буквы: " + isOnlyLetters1); // true
        System.out.println("Только буквы: " + isOnlyLetters2); // false
    }
}

Сравнение с Python: чем похоже и чем отличается

Если ты уже знаком с Python, тебе будет проще. Но есть важные различия, которые нужно запомнить:

Операция Java Python Примечание
Длина строки str.length() len(str) В Java это метод, в Python — функция
Срез строки str.substring(1, 4) str[1:4] В Java — метод, в Python — оператор среза
Поиск подстроки str.indexOf("abc") str.find("abc") Оба возвращают -1, если не найдено
Проверка вхождения str.contains("abc") "abc" in str Python использует оператор in
Разделение строки str.split(",") str.split(",") Очень похоже, но в Java split принимает регулярное выражение
Соединение строк String.join(", ", array) ", ".join(list) Синтаксис почти зеркальный
Замена str.replace("a", "b") str.replace("a", "b") Почти идентично
Регистр str.toUpperCase() str.upper() Разные названия, но смысл тот же
Удаление пробелов str.trim() str.strip() Разные названия
Повторение строки str.repeat(3) str * 3 В Python — оператор, в Java — метод
Форматирование String.format("%s %d", s, i) f"{s} {i}" или "{} {}".format(s, i) В Python более гибкое и современное
Неизменяемость Да, строки immutable Да, строки immutable В обоих языках строки нельзя изменить
💡 Совет: Если ты переходишь с Python на Java, запомни главное: в Java почти всё — методы. Вместо len(str) пиши str.length(), вместо "abc" in str — str.contains("abc"). Привыкнешь за пару дней.

Типичные ошибки и подводные камни

Ошибка 1: Сравнение строк через ==

String a = "Привет";
String b = new String("Привет");
System.out.println(a == b); // false — разные объекты!
System.out.println(a.equals(b)); // true — правильное сравнение

Почему так происходит? == сравнивает ссылки на объекты, а не их содержимое. equals() сравнивает содержимое. Всегда используй equals().

Ошибка 2: Выход за границы строки

String text = "Кот";
char c = text.charAt(3); // ❌ Ошибка! Индекс 3, а длина 3 (индексы 0, 1, 2)
// Правильно: text.charAt(2) — последний символ 'т'

Запомни: Индексы от 0 до length() - 1. Последний символ всегда charAt(length() - 1).

Ошибка 3: Забыл, что строки неизменяемые

String text = "Привет";
text.toUpperCase(); // ❌ Строка не изменилась!
System.out.println(text); // "Привет"

// Правильно:
text = text.toUpperCase(); // ✅ Присваиваем результат обратно
System.out.println(text); // "ПРИВЕТ"

Ошибка 4: Путаница между isEmpty() и isBlank()

String spaces = "   ";
System.out.println(spaces.isEmpty()); // false — есть пробелы
System.out.println(spaces.isBlank()); // true — только пробелы

// Если тебе нужно проверить, есть ли хоть какой-то текст:
if (!spaces.isBlank()) {
    System.out.println("Есть текст");
}

Ошибка 5: Неправильное использование split() с точкой

String ip = "192.168.1.1";
String[] parts = ip.split("."); // ❌ Неправильно! Точка — спецсимвол в regex
// Правильно:
String[] parts = ip.split("\."); // ✅ Экранируем точку

Ошибка 6: Забыл про null

String name = null;
int length = name.length(); // ❌ NullPointerException!

// Всегда проверяй на null:
if (name != null) {
    System.out.println(name.length());
}

Лучшие практики и советы профессионалов

  1. Используй StringBuilder для частых изменений. Если ты много раз меняешь строку (например, в цикле), используй StringBuilder — это в разы быстрее.
  2. // ПЛОХО (создаётся много объектов):
    String result = "";
    for (int i = 0; i < 1000; i++) {
        result += i; // Каждая итерация создаёт новую строку!
    }
    
    // ХОРОШО (один объект):
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 1000; i++) {
        sb.append(i);
    }
    String result = sb.toString();
  3. Сравнивай строк