2009-10-12 3 views
169

Рассмотрим следующий пример.Неизменяемость строк в Java

String str = new String(); 

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

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

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

Редактировать:

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

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

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

Означает ли это, что два объекта будут созданы заново («Миссисипи» и «М сс сс с!!!») И ссылка str указывает на другой объект после replace() метода ?

ответ

244

str не является объектом, это ссылка на объект. "Hello" и "Help!" - это два различных String объектов. Таким образом, strуказывает на строку.Вы можете изменить то, что он указывает на, но не то, что он очка на.

Возьмите этот код, например:

String s1 = "Hello"; 
String s2 = s1; 
// s1 and s2 now point at the same string - "Hello" 

Теперь нет ничего мы могли бы сделать, чтобы 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" 

Редактировать, чтобы ответить на редактирование параметров порядка:

Если вы посмотрите на source code for 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', '!'); :)


На самом деле, это является возможно мутировать строки (и другие неизменные объекты). Это требует отражения и очень, очень опасно и никогда не должно использоваться, если вы действительно не заинтересованы в уничтожении программы.

+11

+1 для вымышленного метода он демонстрирует разницу между неизменным объектом и другими. – Zappi

+1

Спасибо gustafc за правильные примеры и четкое объяснение .... Но можете ли вы просто ответить на отредактированную часть в вопросе, пожалуйста? Это позволит понять мое понимание. –

+13

Я никогда не видел такого ответа раньше. Обсуждали каждую деталь. –

19

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

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

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

Один из способов, которые можно предотвратить str ссылку от изменения, чтобы объявить его как final:

final String STR = "Hello"; 

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

+0

Но в этом случае объект String является «str», который сначала содержит значение «Hello», а затем получает новое значение «Справка!». Что именно вы подразумеваете под «Объекты String, содержащие строки« Hello »и« Help! », Не могут изменить их значения, следовательно, они неизменяемы». ??? Простите меня, если это глупый вопрос. Но я должен был это очистить ... –

+1

У вас есть все, что вы пытались запрограммировать на C? Просто прочтите праймер на указателях, и вы прекрасно поймете ответ Кубинда. –

+0

Смотрите ... Это то, чего я хочу избежать ... Я знаю, что вы отличный программист ... и я просто пытаюсь изучить java здесь ... Поэтому, если вы можете правильно ответить на мой вопрос, пожалуйста, ответьте на него. .. –

6

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

+1

Кратко ответ :-) – RichardAtHome

5

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

3

Неизбежность подразумевает, что значение объекта-объекта не может быть изменено, вы никогда не сможете превратить «Привет» в «Справка!».

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

3

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

9

Light_handle Я рекомендую вам прочитать Cup Size -- a story about variables и Pass-by-Value Please (Cup Size continued). Это очень поможет при чтении сообщений выше.

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

String str = new String(); 

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

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

str --- > "" 

str = "Hello"; 

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

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

str -+ "" 
    +-> "Hello" 

str = "Help!"; 

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

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

str -+ "" 
    | "Hello" 
    +-> "Help!" 
+0

хороший способ показать изменения ссылок. – Stroboskop

+0

+1 рекомендуемые статьи. –

+0

У вас есть "" и "Hello" получить мусор? –

5

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

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! 
0

Для тех, кому интересно, как сломать Струнный неизменность в Java ...

код

import java.lang.reflect.Field; 

public class StringImmutability { 
    public static void main(String[] args) { 
     String str1 = "I am immutable"; 
     String str2 = str1; 

     try { 
      Class str1Class = str1.getClass(); 
      Field str1Field = str1Class.getDeclaredField("value"); 

      str1Field.setAccessible(true); 
      char[] valueChars = (char[]) str1Field.get(str1); 

      valueChars[5] = ' '; 
      valueChars[6] = ' '; 

      System.out.println(str1 == str2); 
      System.out.println(str1); 
      System.out.println(str2);   
     } catch (NoSuchFieldException e) { 
      e.printStackTrace(); 
     } catch (SecurityException e) { 
      e.printStackTrace(); 
     } catch (IllegalArgumentException e) { 
      e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

Outp ut

true 
I am mutable 
I am mutable 
0

В Java к объектам обычно обращаются ссылки. В вашем фрагменте кода str является ссылкой, которая сначала назначается «Hello» (автоматически созданный объект или получен из constant pool), а затем вы назначили другой объект «Справка!». к той же ссылке. Следует отметить, что ссылка является той же и измененной, но объекты разные. Еще одна вещь в вашем коде вы получили доступ к трем объектам,

  1. Когда вы вызвали новую строку().
  2. Когда вы назначили «привет».
  3. Когда вы назначили «помощь!».

Вызов нового объекта String() создает новый объект, даже если он существует в пуле строк, поэтому его обычно не следует использовать. Чтобы поместить строку, созданную из нового String() в пул строк, вы можете попробовать метод intern().

Надеюсь, это поможет.

0

String is непреложный. Это означает, что мы можем изменить только ссылку .


String a = "a"; 
System.out.println("String a is referencing to "+a); // Output: a 

a.concat("b"); 
System.out.println("String a is referencing to "+a); // Output: a 

a = a.concat("b"); 
System.out.println("String a has created a new reference and is now referencing to "+a); // Output: ab 
0

Неизменность Я могу сказать, что вы не можете изменить саму строку. Предположим, что у вас есть String x, значение которого равно «abc». Теперь вы не можете изменить строку, т. Е. Вы не можете изменить символ/с в «abc».

Если вам нужно изменить любой символ/s в String, вы можете использовать массив символов и изменить его или использовать StringBuilder.

String x = "abc"; 
x = "pot"; 
x = x + "hj"; 
x = x.substring(3); 
System.out.println(x); 

char x1[] = x.toCharArray(); 
x1[0] = 's'; 
String y = new String(x1); 
System.out.println(y); 

Выход:

hj 
sj 
2

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

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

Если вы видите здесь, я использую метод concat изменить исходную строку, то есть, "Новая строка" со строкой «Добавлено 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 
6

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

String s1 = "hello"; 

Это заявление создает строку, содержащую привет и занимают место в памяти, то есть в Constant Струнный Бассейн и и назначили его ссылку на объект s1

String s2 = s1; 

Thi s оператор присваивает ту же строку привет к новой ссылке s2

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

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

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

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

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

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

  __________ 
     |   | 
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 

Теперь вы можете быть удивлены, почему строка имеет такие методы, как CONCAT() изменить. Следующий фрагмент очистит ваше замешательство.

s1 = s1.concat(" overflow"); 

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

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


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

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

2

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

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

создаст одну строку «Миссисипи» и добавит его в строку бассейн так что теперь ул наведен на Миссисипи.

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

Но после указанной выше операции одна другая строка будет создана «M! Сс! Сс! Рр!» и он будет добавлен в String pool. и теперь str указывает на M! ss! ss! pp !, а не на Миссисипи.

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

Позволяет иметь еще один пример

String s1 = "Hello"; 
String s2 = "World"; 
String s = s1 + s2; 

это выше три линии добавит три объекта строки в строку пула.
1) Здравствуйте
2) Мир
3) HelloWorld

+0

Хорошее объяснение ......... –

0

Или вы можете попробовать:

public class Tester 
{ 
public static void main(String[] args) 
{ 
String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 
System.out.println(str.hashCode()); 

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

Это покажет, как изменения Hashcode.

2

Как Линус Tolvards сказал:

Обсуждение дешево.Покажите мне код

Взгляните на это:

public class Test{ 
    public static void main(String[] args){ 

     String a = "Mississippi"; 
     String b = "Mississippi";//String immutable property (same chars sequence), then same object 

     String c = a.replace('i','I').replace('I','i');//This method creates a new String, then new object 
     String d = b.replace('i','I').replace('I','i');//At this moment we have 3 String objects, a/b, c and d 

     String e = a.replace('i','i');//If the arguments are the same, the object is not affected, then returns same object 

     System.out.println("a==b? " + (a==b)); // Prints true, they are pointing to the same String object 

     System.out.println("a: " + a); 
     System.out.println("b: " + b); 

     System.out.println("c==d? " + (c==d)); // Prints false, a new object was created on each one 

     System.out.println("c: " + c); // Even the sequence of chars are the same, the object is different 
     System.out.println("d: " + d); 

     System.out.println("a==e? " + (a==e)); // Same object, immutable property 
    } 
} 

Выход

a==b? true 
a: Mississippi 
b: Mississippi 
c==d? false 
c: Mississippi 
d: Mississippi 
a==e? true 

Итак, помните две вещи:

  • Строки неизменны, пока вы применить метод, который манипулирует и создает новый (c & d случаев).
  • Заменить метод возвращает тот же объект String, если оба параметра являются одинаковыми
2

Строка в Java в непреложных и Final просто означает, что она не может быть изменена или модифицирована:

Case 1:

class TestClass{ 
public static void main(String args[]){ 
    String str = "ABC"; 
    str.concat("DEF"); 
    System.out.println(str); 
} 
} 

Outp ет: ABC

Причина: Ссылка на объект ул не меняются на самом деле новый объект «DEF» создан, который находится в бассейне и не имеют никаких ссылок на всех (т.е. утрачена).

Случай 2:

class TestClass{ 
public static void main(String args[]){ 
    String str="ABC"; 
    str=str.concat("DEF"); 
    System.out.println(str); 
} 
} 

Выход: ABCDEF

Причина: В этом случае ул теперь со ссылкой на новый объект "ABCDEF" поэтому она печатает ABCDEF т.е. предыдущий str объект «ABC» теряется в пуле без ссылки.

0

Строка непреложная означает, что вы не можете изменить сам объект, но вы можете изменить ссылку на объект. Когда вы вызываете a = "ty", вы фактически меняете ссылку a на новый объект, созданный строковым литералом "ty". Изменение объекта означает использовать его методы для изменения одного из ее полей (или поля являются общедоступными и не является окончательным, так что они могут быть обновлены снаружи без доступа к ним с помощью методов), например:

Foo x = new Foo("the field"); 
x.setField("a new field"); 
System.out.println(x.getField()); // prints "a new field" 

While в неизменяемом классе (объявлен как final, чтобы предотвратить модификацию через наследование) (его методы не могут изменять свои поля, а также поля всегда являются частными и рекомендуются быть окончательными), например String, вы не можете изменить текущую строку, но можете вернуть новую строку, то есть:

String s = "some text"; 
s.substring(0,4); 
System.out.println(s); // still printing "some text" 
String a = s.substring(0,4); 
System.out.println(a); // prints "some" 
0

Здесь непреложность означает, что экземпляр может указывать на другую ссылку, но исходное содержимое строки не будет изменено в исходной ссылке. Позвольте мне объяснить в первом примере, данном вами. Первая строка указывает на «Hello», ее Ok до этого. Второй раз его указывали на «Помощь!». Здесь str начал указывать на «Помощь!», и ссылка на строку« Hello »потеряна, и мы не сможем ее вернуть.

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

0

Сверху до ответа, но хотелось поставить краткое сообщение от автора класса String в Java

Строки постоянны, их значения ca Не будут изменены после создания . Буферы String поддерживают изменяемые строки. Поскольку объекты String неизменяемы, они могут использоваться совместно.

Он может быть получен из этой документации, что все, что изменяет строку, возвращает других объекта (который может быть новым или интернированы и старым). Не столь тонкий намек на это должен исходить от сигнатуры функции. Подумайте об этом: «Почему они сделали функцию объекта возвращением объекта вместо статуса?».

public String replace(char oldChar, char newChar) 

Также еще один источник, который делает это поведение явным (от замены документации функции)

Возвращает новую строку, в результате замены всех вхождений oldChar в этой строке с newChar.

Источник: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replace(char,%20char)

  • автор Ли Бойнтон
  • автор Артур ван Хофф
  • автор Мартин Бухгольц
  • автор Ulf Zibis

Источник: JavaDoc из String.

0

Строка объекта - сама процедура сделана «неизменной». Это действие не производит никаких изменений: «letters.replace (« bbb »,« aaa »);

Но назначение данных действительно вызывает изменения в содержание Струны для изменения:

letters = "aaa"; 
    letters=null; 
    System.out.println(letters); 
    System.out.println(oB.hashCode()); 
    System.out.println(letters); 
    letters = "bbbaaa"; 
    System.out.println(oB.hashCode()); 
    System.out.println(letters); 

// хэш строкового объекта не изменяется.

1

Поскольку String неизменен, поэтому изменений не произойдет, если вы не присвоите возвращаемое значение функции string.so в вопросе присваиваете значение функции swap возвращенного значения s.

s = swap (s, n1, n2), после чего значение строки s изменится.

Я также получал неизменность значения, когда я писал программу, чтобы получить некоторые перестановки строки (Хотя это не дает все перестановки, но это, например, чтобы ответить на ваш вопрос)

Вот пример.

> import java.io.*;  public class MyString { public static void 
> main(String []args)throws IOException {  BufferedReader br=new 
> BufferedReader(new InputStreamReader(System.in));  String 
> s=br.readLine().trim(); int n=0;int k=0;  while(n!=s.length()) { 
> while(k<n){  swap(s,k,n); System.out.println(s); swap(s,k,n); k++; } 
> n++; } }  public static void swap(String s,int n1,int n2) { char temp; 
> temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); 
> sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); 
> } } 

, но я не получал переставленное значение строки из выше code.So я присвоил возвращенное значение функции обмена в строку и переоделся значение строки. после присвоения возвращаемого значения я получил перестановочные значения строки.

/import java.util.*; import java.io.*; public class MyString { public static void main(String []args)throws IOException{ 
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
String s=br.readLine().trim(); int n=0;int k=0;  
while(n!=s.length()){ while(k<n){ s=swap(s,k,n);  
System.out.println(s); s=swap(s,k,n); k++; } n++; } }  
public static String swap(String s,int n1,int n2){ 
char temp; temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); return s; } } 
Смежные вопросы