Неизменяемость строк в Java
Рассмотрим следующий пример.
String str = new String();
str = "Hello";
System.out.println(str); //Prints Hello
str = "Help!";
System.out.println(str); //Prints Help!
Теперь, в Java, строки неизменяемы. Тогда почему объекту str
может быть присвоено другое значение, например "Help!". Не противоречит ли это неизменяемости строк в Java? Кто-нибудь, пожалуйста, может объяснить мне точную концепцию неизменяемости?
Редактировать:
ОК. Теперь я понял, но только один последующий вопрос. Как насчет следующего кода:
String str = "Mississippi";
System.out.println(str); // prints Mississippi
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
Означает ли это, что два объекта создаются снова ("Mississippi" и "M!ss!ss!pp!"), а ссылка str
указывает на другой объект после replace()
метода?
Переведено автоматически
Ответ 1
str
это не объект, это ссылка на объект. "Hello"
и "Help!"
это два разных String
объекта. Таким образом, str
указывает на строку. Вы можете изменить то, на что оно указывает, но не то, на что оно указывает.
Возьмем, к примеру, этот код:
String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"
Теперь нет ничего, что 1 мы могли бы сделать с s1
, что повлияло бы на значение s2
. Они ссылаются на один и тот же объект - строку "Hello"
- но этот объект неизменяем и, следовательно, не может быть изменен.
Если мы сделаем что-то вроде этого:
s1 = "Help!";
System.out.println(s2); // still prints "Hello"
Здесь мы видим разницу между изменением объекта и изменением ссылки. s2
по-прежнему указывает на тот же объект, на который мы изначально указалиs1
. Установка s1
в "Help!"
значение изменяет только ссылку, в то время как String
объект, на который она первоначально ссылалась, остается неизменным.
Если бы строки были изменяемыми, мы могли бы сделать что-то вроде этого:
String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"
Отредактируйте, чтобы ответить на правку OP:
Если вы посмотрите на исходный код String.replace(char,символ) (также доступен в src.zip в вашем каталоге установки JDK ( профессиональный совет - заглядывать туда всякий раз, когда вам интересно, как что-то действительно работает) вы можете увидеть, что это делает следующее:
- Если в текущей строке есть одно или несколько вхождений
oldChar
, создайте копию текущей строки, в которой все вхожденияoldChar
заменяются наnewChar
. - Если
oldChar
отсутствует в текущей строке, верните текущую строку.
Итак, да, "Mississippi".replace('i', '!')
создает новый String
объект. Опять же, выполняется следующее:
String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects
Ваше домашнее задание на данный момент - посмотреть, что сделает приведенный выше код, если вы измените s1 = s1.replace('i', '!');
на s1 = s1.replace('Q', '!');
:)
1 На самом деле, есть возможность изменять строки (и другие неизменяемые объекты). Это требует осмысления и очень, очень опасно, и его никогда не следует использовать, если вы действительно не заинтересованы в уничтожении программы.
Ответ 2
Объект, на который str
ссылаются, может изменяться, но сами фактические String
объекты изменяться не могут.
String
Объекты, содержащие строку "Hello"
и "Help!"
, не могут изменять свои значения, следовательно, они неизменяемы.
Неизменяемость String
объектов не означает, что ссылки, указывающие на объект, не могут изменяться.
Один из способов предотвратить изменение str
ссылки - объявить ее как final
:
final String STR = "Hello";
Теперь попытка присвоить другое String
значение STR
вызовет ошибку компиляции.
Ответ 3
Light_handle Я рекомендую вам ознакомиться с Cup Size - рассказом о переменных и передаче по значению, пожалуйста (Размер чашки продолжается). Это очень поможет при чтении сообщений выше.
Вы их читали? ДА. Хорошо.
String str = new String();
Это создает новый "удаленный элемент управления" с именем "str
" и присваивает ему значение new String()
(или ""
).
например, в памяти это создает:
str --- > ""
str = "Hello";
Затем это изменяет удаленный элемент управления "str
", но не изменяет исходную строку ""
.
например, в памяти это создает:
str -+ ""
+-> "Hello"
str = "Help!";
Затем это изменяет удаленный элемент управления "str
", но не изменяет исходную строку ""
или объект, на который в данный момент указывает удаленный элемент управления.
например, в памяти это создает:
str -+ ""
| "Hello"
+-> "Help!"
Ответ 4
Давайте разберем это на несколько частей
String s1 = "hello";
Этот оператор создает строку, содержащую hello, и занимает место в памяти, т.е. в пуле постоянных строк, и присваивает ее объекту-ссылке s1
String s2 = s1;
Этот оператор присваивает ту же строку hello новой ссылке s2
__________
| |
s1 ---->| hello |<----- s2
|__________|
Обе ссылки указывают на одну и ту же строку, поэтому выведите одно и то же значение следующим образом.
out.println(s1); // o/p: hello
out.println(s2); // o/p: hello
Хотя строка неизменяема, присвоение может быть возможным, поэтому s1 теперь будет ссылаться на новое значение stack.
s1 = "stack";
__________
| |
s1 ---->| stack |
|__________|
Но как насчет объекта s2, который указывает на hello, все будет как есть.
__________
| |
s2 ---->| hello |
|__________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
Поскольку строка неизменяема, виртуальная машина Java не позволит нам изменять строку s1 своим методом. Она создаст все новые объекты String в пуле следующим образом.
s1.concat(" overflow");
___________________
| |
s1.concat ----> | stack overflow |
|___________________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
out.println(s1.concat); // o/p: stack overflow
Примечание, если строка была бы изменяемой, то результат был бы
out.println(s1); // o/p: stack overflow
Теперь вы можете быть удивлены, почему для изменения String используются такие методы, как concat() . Следующий фрагмент прояснит ваше замешательство.
s1 = s1.concat(" overflow");
Здесь мы присваиваем измененное значение string обратно ссылке s1.
___________________
| |
s1 ---->| stack overflow |
|___________________|
out.println(s1); // o/p: stack overflow
out.println(s2); // o/p: hello
Вот почему Java решила, что String будет конечным классом В противном случае любой может модифицировать и изменять значение string .
Надеюсь, это немного поможет.