Неизменность строк в 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? Кто-нибудь может объяснить мне точную концепцию неизменности?
Редактировать:
Хорошо. Теперь я понимаю, но только один дополнительный вопрос. Как насчет следующего кода:
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()
метод?
24 ответа
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"
Изменить, чтобы ответить на редактирование 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 На самом деле, можно изменять строки (и другие неизменные объекты). Это требует размышлений и очень, очень опасно и никогда не должно использоваться, если вы на самом деле не заинтересованы в уничтожении программы.
Объект, который str
ссылки могут измениться, но фактические String
Сами объекты не могут.
String
объекты, содержащие строку "Hello"
а также "Help!"
не может изменить свои значения, поэтому они неизменны.
Неизменность String
Объект не означает, что ссылки, указывающие на объект, не могут быть изменены.
Один способ, которым можно предотвратить str
ссылка от изменения, чтобы объявить его final
:
final String STR = "Hello";
Теперь пытаюсь назначить другую String
в STR
вызовет ошибку компиляции.
Light_handle Я рекомендую вам прочитать " Размер чашки" - рассказ о переменных и Проходном значении, пожалуйста (Продолжение размера чашки). Это очень поможет при чтении постов выше.
Читали ли вы их? Да. Хорошо.
String str = new String();
Это создает новый "пульт дистанционного управления" под названием "str
"и устанавливает это значение new String()
(или же ""
).
например, в памяти это создает:
str --- > ""
str = "Hello";
Это затем меняет пульт дистанционного управления "str
"но не изменяет исходную строку ""
,
например, в памяти это создает:
str -+ ""
+-> "Hello"
str = "Help!";
Это затем меняет пульт дистанционного управления "str
"но не изменяет исходную строку ""
или объект, на который в данный момент указывает пульт дистанционного управления.
например, в памяти это создает:
str -+ ""
| "Hello"
+-> "Help!"
Давайте разбить его на несколько частей
String s1 = "hello";
Этот оператор создает строку, содержащую привет и занимающую место в памяти, т. Е. В пуле строк констант, и назначает ее ссылочному объекту s1.
String s2 = s1;
Этот оператор присваивает ту же строку привет новой ссылке s2
__________
| |
s1 ---->| hello |<----- s2
|__________|
Обе ссылки указывают на одну и ту же строку, поэтому выведите одно и то же значение следующим образом.
out.println(s1); // o/p: hello
out.println(s2); // o/p: hello
Хотя String является неизменяемым, присваивание может быть возможным, поэтому s1 теперь будет ссылаться на новый стек значений.
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 является конечным классом. В противном случае любой может изменить и изменить значение строки. Надеюсь, это поможет немного.
Строковый объект, на который впервые ссылается str
не было изменено, все, что вы сделали, это сделать str
ссылаться на новый строковый объект.
Строка не изменится, ссылка на нее изменится. Вы путаете неизменность с понятием final
поля. Если поле объявлено как final
после того, как он был назначен, он не может быть переназначен.
Что касается замены части вашего вопроса, попробуйте это:
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!
Хотя ява пытается игнорировать это, str
не более чем указатель Это означает, что когда вы впервые пишете str = "Hello";
, вы создаете объект, который str
указывает на. Когда вы переназначаете str
написав str = "Help!";
, новый объект создан и старый "Hello"
объект получает мусор всякий раз, когда Java чувствует, что это нравится.
Неизменность подразумевает, что значение экземпляра объекта не может измениться, вы никогда не сможете превратить "Hello" в "Help!".
Переменная str является ссылкой на объект, когда вы присваиваете str новое значение, вы не изменяете значение объекта, на который она ссылается, вы ссылаетесь на другой объект.
Строковый класс является неизменным, и вы не можете изменить значение неизменяемого объекта. Но в случае с String, если вы измените значение строки, это создаст новую строку в пуле строк, а затем ваша строковая ссылка на это значение, а не старое. таким образом, строка является неизменной. Давайте возьмем ваш пример,
String str = "Mississippi";
System.out.println(str); // prints Mississippi
он создаст одну строку "Миссисипи" и добавит ее в пул строк, поэтому теперь str указывает на Миссисипи.
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
Но после вышеуказанной операции будет создана еще одна строка "M! Ss! Ss! Pp!" и он будет добавлен в пул строк. и теперь str указывает на M!ss!ss!pp!, а не на Миссисипи.
таким образом, когда вы измените значение строкового объекта, он создаст еще один объект и добавит его в пул строк.
Давайте еще один пример
String s1 = "Hello";
String s2 = "World";
String s = s1 + s2;
эта строка из трех выше добавит три объекта строки в пул строк.
1) Привет
2) Мир
3) HelloWorld
Использование:
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
Для тех, кто интересуется, как нарушить неизменность String в 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();
}
}
}
Выход
true
I am mutable
I am mutable
Как сказал Линус Толвардс:
Обсуждение дешево. Покажи мне код
Взгляните на это:
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).
- Метод Replace возвращает один и тот же объект String, если оба параметра совпадают
Здесь неизменность означает, что экземпляр может указывать на другую ссылку, но исходное содержимое строки не будет изменено в исходной ссылке. Позвольте мне объяснить первым примером, данным вами. Первая строка указывает на "Привет", все в порядке до этого. Второй раз это указывает на "Помощь!". Здесь ул начал указывать на "Помощь!" и ссылка на строку "Hello" потеряна, и мы не можем получить это обратно.
Фактически, когда str попытается изменить существующее содержимое, тогда будет сгенерирована другая новая строка, и str начнет указывать на эту ссылку. Таким образом, мы видим, что строка в исходной ссылке не изменяется, но это безопасно при ее ссылке, и экземпляр объекта начинает указывать на другую ссылку, поэтому неизменность сохраняется.
Строка Object - методы сами по себе сделаны "неизменяемыми". Это действие не приводит к изменениям: "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);
// Хеш-код строки Object не изменяется.
Слишком поздно для ответа, но хотел выложить краткое сообщение от автора класса String в Java
Строки постоянны; их значения не могут быть изменены после их создания. Строковые буферы поддерживают изменяемые строки. Поскольку объекты String являются неизменяемыми, они могут использоваться совместно.
Из этой документации можно сделать вывод, что все, что изменяет строку, возвращает другой объект (который может быть новым или интернированным и старым). Не очень тонкий намек на это должен исходить из сигнатуры функции. Подумайте об этом: "Почему они заставляют функцию возвращать объект вместо статуса?".
public String replace(char oldChar, char newChar)
Также еще один источник, который делает это поведение явным (из документации по замене функций)
Возвращает новую строку, полученную в результате замены всех вхождений oldChar в этой строке на newChar.
Источник: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html
- автор Ли Бойнтон
- автор Артур ван Хофф
- автор Мартин Буххольц
- автор Ульф Зибис
Источник: JavaDoc String.
Неизменность Я могу сказать, что вы не можете изменить саму строку. Предположим, у вас есть строка x, значение которой равно "abc". Теперь вы не можете изменить строку, то есть вы не можете изменить любой символ (ы) в "abc".
Если вам нужно изменить какой-либо символ / ы в строке, вы можете использовать массив символов и изменить его или использовать 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
Строка неизменна. Это означает, что мы можем только изменить ссылку.
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
Я бы объяснил это простым примером
рассмотрите любой массив символов: например, char a[]={'h','e', 'l','l','o'}; и строка: String s="hello";
с массивом символов мы можем выполнять такие операции, как печать только последних трех букв, используя итерацию массива; но в строке мы должны создать новый объект String и скопировать требуемую подстроку, и ее адрес будет в новом строковом объекте.
например
***String s="hello";
String s2=s.substrig(0,3);***
так что s2 будет иметь "hel";
Строка неизменна означает, что вы не можете изменить сам объект, но вы можете изменить ссылку на объект. Когда вы вызываете a = "ty", вы фактически изменяете ссылку a на новый объект, созданный строковым литералом "ty". Изменение объекта означает использование его методов для изменения одного из его полей (или поля являются общедоступными и не являются окончательными, чтобы их можно было обновлять извне, не обращаясь к ним через методы), например:
Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"
Находясь в неизменяемом классе (объявленном как 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"
Или вы можете попробовать:
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());
}
}
Это покажет, как изменяется хэш-код.
Если HELLO
ваша строка, то вы не можете изменить HELLO
в HILLO
, Это свойство называется свойством неизменности.
Вы можете иметь несколько указателей на строковую переменную, чтобы указывать HELLO String.
Но если HELLO - char Array, тогда вы можете изменить HELLO на HILLO. Например,
char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this
Языки программирования имеют неизменные переменные данных, так что их можно использовать в качестве ключей в ключе, паре значений.
В Java к объектам обычно обращаются по ссылкам. В вашем фрагменте кода str есть ссылка, которая сначала присваивается "Hello" (автоматически созданный объект или извлекается из постоянного пула), а затем вы назначаете другой объект "Help!" к той же ссылке. Следует отметить, что ссылка такая же и изменена, но объекты разные. Еще одна вещь в вашем коде вы получили доступ к трем объектам,
- Когда вы вызвали новую строку ().
- Когда ты назначил "привет".
- Когда ты назначил "помощь!".
Вызов new String() создает новый объект, даже если он существует в пуле строк, поэтому обычно его не следует использовать. Чтобы поместить строку, созданную из новой String () в пул строк, вы можете попробовать intern()
метод.
Надеюсь, это поможет.
public final class String_Test {
String name;
List<String> list=new ArrayList<String>();
public static void main(String[] args) {
String_Test obj=new String_Test();
obj.list.add("item");//List will point to a memory unit- i.e will have one Hashcode value #1234
List<String> list2=obj.list; //lis1 also will point to same #1234
obj.list.add("new item");//Hashcode of list is not altered- List is mutable, so reference remains same, only value in that memory location changes
String name2=obj.name="Myname"; // name2 and name will point to same instance of string -Hashcode #5678
obj.name = "second name";// String is Immutable- New String HAI is created and name will point to this new instance- bcoz of this Hashcode changes here #0089
System.out.println(obj.list.hashCode());
System.out.println(list2.hashCode());
System.out.println(list3.hashCode());
System.out.println("===========");
System.out.println(obj.name.hashCode());
System.out.println(name2.hashCode());
}
}
Буду выкладывать ставить что то подобное
1419358369 1419358369
103056 65078777
Цель неизменяемого объекта состоит в том, что его значение не должно изменяться после назначения. Он будет возвращать новый объект каждый раз, когда вы пытаетесь изменить его в зависимости от реализации. Примечание: Stringbuffer вместо строки может быть использован, чтобы избежать этого.
К вашему последнему вопросу:: у вас будет одна ссылка и 2 строки в пуле строк. За исключением ссылки будет указывать на m! Ss! Ss! Pp!
Строка в Java в Immutable и Final означает, что ее нельзя изменить или изменить:
Случай 1:
class TestClass{
public static void main(String args[]){
String str = "ABC";
str.concat("DEF");
System.out.println(str);
}
}
Выход: ABC
Причина: ссылка на объект str не изменяется, фактически создается новый объект "DEF", который находится в пуле и не имеет ссылки вообще (т.е. потерян).
Случай 2:
class TestClass{
public static void main(String args[]){
String str="ABC";
str=str.concat("DEF");
System.out.println(str);
}
}
Выход: ABCDEF
Причина: в этом случае str теперь ссылается на новый объект "ABCDEF", следовательно, он печатает ABCDEF, т. Е. Предыдущий объект str "ABC" теряется в пуле без ссылки.
Поскольку String является неизменяемым, изменения не произойдут, если вы не будете присваивать возвращаемое значение функции для string.so. В своем вопросе присвойте возвращаемое значение функции подкачки 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();
> } }
но я не получал переставленные значения строки из приведенного выше кода. Поэтому я присвоил возвращаемое значение функции подкачки строке и получил измененные значения строки. после присвоения возвращенного значения я получил перестановочные значения строки.
/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; } }