Неизменность строк в Java - PullRequest
205 голосов
/ 12 октября 2009

Рассмотрим следующий пример.

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

Теперь в Java объекты String неизменны. Тогда как же объекту str может быть присвоено значение «Помогите!». Разве это не противоречит неизменности строк в Java? Кто-нибудь может объяснить мне точную концепцию неизменности?

Edit:

Ok. Теперь я понимаю, но только один дополнительный вопрос. А как насчет следующего кода:

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

Означает ли это, что два объекта создаются снова ("Миссисипи" и "M! Ss! Ss! Pp!"), А ссылка str указывает на другой объект после метода replace()?

Ответы [ 25 ]

291 голосов
/ 12 октября 2009

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, 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 На самом деле, можно изменять строки (и другие неизменные объекты). Это требует размышлений и очень, очень опасно и никогда не должно использоваться, если вы на самом деле не заинтересованы в уничтожении программы.

23 голосов
/ 12 октября 2009

Объект, на который str ссылается, может измениться, но сами объекты String не могут.

Объекты String, содержащие строки "Hello" и "Help!", не могут изменять свои значения, поэтому они неизменны.

Неизменность String объектов не означает, что ссылки, указывающие на объект, не могут измениться.

Один из способов предотвратить изменение ссылки str - это объявить ее final:

final String STR = "Hello";

Теперь попытка назначить другой String на STR приведет к ошибке компиляции.

9 голосов
/ 12 октября 2009

Light_handle Я рекомендую вам прочитать Размер чашки - рассказ о переменных и Передача по значению Пожалуйста (Размер чашки продолжен) . Это очень поможет при чтении постов выше.

Ты их читал? Да. Хорошо.

String str = new String();

Это создает новый «пульт дистанционного управления» под названием «str» и устанавливает для него значение new String() (или "").

например. в памяти это создает:

str --- > ""

str  = "Hello";

Это затем изменяет пульт дистанционного управления "str", но не изменяет исходную строку "".

например. в памяти это создает:

str -+   ""
     +-> "Hello"

str = "Help!";

Это затем изменяет пульт ДУ "str", но не изменяет исходную строку "" или объект, на который в данный момент указывает пульт ДУ.

например. в памяти это создает:

str -+   ""
     |   "Hello"
     +-> "Help!"
7 голосов
/ 18 мая 2016

Позволяет разбить его на несколько частей

String s1 = "hello";

Этот оператор создает строку, содержащую hello , и занимает место в памяти, т. Е. В пуле константных строк , и назначает ее ссылочному объекту s1

String s2 = s1;

Этот оператор назначает ту же строку привет новой ссылке s2

         __________
        |          |
s1 ---->|  hello   |<----- s2
        |__________| 

Обе ссылки указывают на одну и ту же строку, поэтому выведите одно и то же значение следующим образом.

out.println(s1);    // o/p: hello
out.println(s2);    // o/p: hello

Хотя String является неизменным , назначение может быть возможным, поэтому s1 теперь будет ссылаться на новое значение stack .

s1 = "stack";    
         __________
        |          |
s1 ---->|  stack   |
        |__________|

Но как насчет s2 объекта, который указывает на привет , он будет таким, как есть.

         __________
        |          |
s2 ---->|  hello   |
        |__________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello

Поскольку String является неизменным Виртуальная машина 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");

Здесь мы присваиваем измененное значение строки назад s1 ссылка.

         ___________________
        |                   |
s1 ---->|  stack overflow   |
        |___________________|


out.println(s1);    // o/p: stack overflow
out.println(s2);    // o/p: hello

Вот почему Java решила String быть конечным классом В противном случае любой может изменить и изменить значение строки. Надеюсь, это поможет немного.

6 голосов
/ 12 октября 2009

Строковый объект, на который впервые ссылается str, не был изменен, все, что вы сделали, это заставили str обратиться к новому строковому объекту.

5 голосов
/ 12 октября 2009

Что касается замены части вашего вопроса, попробуйте это:

String str = "Mississippi"; 
System.out.println(str); //Prints Mississippi 

String other = str.replace("i", "!"); 
System.out.println(str); //still prints Mississippi 
System.out.println(other);  // prints M!ss!ss!pp!
5 голосов
/ 12 октября 2009

Строка не изменится, ссылка на нее изменится. Вы путаете неизменность с понятием final полей. Если поле объявлено как final, после назначения оно не может быть переназначено.

3 голосов
/ 12 октября 2009

Хотя java пытается это игнорировать, str - не более чем указатель. Это означает, что когда вы впервые пишете str = "Hello";, вы создаете объект, на который указывает str. Когда вы переназначаете str, записывая str = "Help!";, создается новый объект, и старый объект "Hello" получает мусор всякий раз, когда java чувствует себя так, как он.

3 голосов
/ 12 октября 2009

Неизменность подразумевает, что значение экземпляра объекта не может измениться, вы никогда не сможете превратить «Hello» в «Help!».

Переменная str является ссылкой на объект, когда вы присваиваете str новое значение, вы не изменяете значение объекта, на который он ссылается, вы ссылаетесь на другой объект.

2 голосов
/ 06 февраля 2016

Использование:

String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String

Если вы видите здесь, я использую метод concat для изменения исходной строки, то есть «New String» со строкой «Added String», но все же я получил вывод, как и предыдущий, следовательно, это доказывает, что вы можете не изменяйте ссылку на объект класса String, но если вы сделаете это с помощью класса StringBuilder, это сработает. Он указан ниже.

StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...