Строки неизменяемы
В прошлом уроке мы сразу печатали результат каждого метода. А что будет, если вызвать метод и никуда не положить его результат?
Вспомните напечатанную карточку. Исправить на ней букву нельзя. Можно только сделать новую карточку с нужными буквами и отдать её вместо старой. Так ведут себя все методы строки, которые выглядят как изменение:
public class Main {
public static void main(String[] args) {
String name = "anna";
name.replace('a', 'o');
System.out.println(name); // anna
String other = name.replace('a', 'o');
System.out.println(other); // onno
System.out.println(name); // anna
}
}Давайте разберём программу по строкам:
name.replace('a', 'o')создаёт новую строкуonno, где каждаяaзаменена наo. Но её никуда не положили, и она пропадает.- Первый
println(name)печатаетanna: строкаnameне изменилась. String other = name.replace('a', 'o');снова создаёт строкуonnoи на этот раз кладёт её вother.println(other)печатаетonno.- Последний
println(name)печатаетanna: новая строка появилась, а старая осталась прежней.
Строка не меняется
Объект, который после создания нельзя изменить, называют неизменяемым. Строка в Java неизменяема: ни один её метод не трогает её символы. Методы, которые выглядят как изменение, возвращают новую строку:
toUpperCase()иtoLowerCase();replace(…);strip();substring(…).
Если результат такого метода не сохранить, он пропадёт. Компилятор об этом не предупреждает: вызвать метод и не взять его результат в Java можно.
Новое значение — в ту же переменную
Чтобы в переменной оказался изменённый текст, результат кладут в неё же: name = name.toUpperCase();. Строка при этом по-прежнему не меняется — меняется переменная. Посмотрим, что это значит:
String city = "paris";
String before = city;
city = city.toUpperCase();
System.out.println(city); // PARIS
System.out.println(before); // parisРазберём по строкам:
String city = "paris";кладёт вcityссылку на строкуparis. Переменная типаString, как и переменная-массив, хранит не сам объект, а ссылку на него: не карточку, а талон к ней.String before = city;копирует ссылку: обе переменные ведут к одной строкеparis.city = city.toUpperCase();создаёт новую строкуPARISи кладёт вcityссылку на неё.println(city)печатаетPARIS.println(before)печатаетparis:beforeведёт к старой строке, а её никто не менял.
С массивом вышло бы иначе: запись через одну ссылку видна через другую. Со строкой так не бывает, потому что её символы не может поменять никто. Так же работает и +=: запись city += "!" значит city = city + "!" и тоже создаёт новую строку.
Здесь картинка с карточкой неточна. Ненужную карточку в жизни кто-то должен выбросить. В Java строку, к которой не ведёт ни одна ссылка, JVM убирает из памяти сама.
Зачем строки неизменяемы
Этот вопрос часто задают на собеседовании. Главный ответ: неизменяемую строку можно безопасно делить.
- Одну строку могут хранить несколько переменных, и ни одна из них не испортит её для других.
- Строку можно передать в метод: что бы он ни делал, ваша строка останется прежней.
- Поэтому Java может хранить одинаковые строки в одном экземпляре и экономить память.
final — это другое
Слово final и неизменяемость строки часто путают, но запрещают они разное:
finalзапрещает переменной получить новое значение, то есть другую ссылку;- неизменяемость запрещает менять саму строку.
final String greeting = "hi";
greeting = greeting.toUpperCase(); // ✗error: cannot assign a value to final variable greetingСоздать строку HI здесь можно, а положить её в final-переменную — нельзя.
Частая ошибка
Вызвали метод и забыли сохранить результат:
String input = " yes ";
input.strip(); // ✗
System.out.println(input.length()); // 6Хотели убрать пробелы, а длина осталась 6. Метод strip() вернул новую строку yes, и она пропала. Сохраните результат: после input = input.strip(); вызов input.length() даёт 3.
Главное
- Строка неизменяема: её методы не меняют символы, а возвращают новую строку.
- Результат метода нужно сохранить, иначе он пропадёт:
name = name.toUpperCase();. - Переменная типа
Stringхранит ссылку; присваивание меняет ссылку, а не строку. finalзапрещает менять переменную, а неизменяемость — саму строку.