Строка неизменна. В чем же смысл?
Я написал следующий код на неизменяемых строках.
public class ImmutableStrings { public static void main(String[] args) { testmethod(); } private static void testmethod() { String a = "a"; System.out.println("a 1-->" + a); a = "ty"; System.out.println("a 2-->" + a); } }
Вывод:
a 1-->aa 2-->ty
Здесь значение переменной a
было изменено (в то время как многие говорят, что содержимое неизменяемых объектов не может быть изменено). Но что именно означает, что String
является неизменным ? Не могли бы вы прояснить эту тему для меня?
- Игнорирование акцентированных букв в сравнении строк
- Подстрочный индекс и надстрочный указатель строки в Android
- Как работает строковая подстрока в Swift
- Есть ли на C # String Tokenizer, например, Java?
- Как добавить текст в изображение в java?
источник: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
- String.Format не работает в TypeScript
- Эффективный способ сравнения строк версии в Java
- Строковые литералы: куда они идут?
- Равная высота элементов внутри элемента сетки с раскладкой сетки CSS
- Проверьте, соответствует ли строка регулярному выражению в сценарии Bash
- Java: String split (): Я хочу, чтобы он включал пустые строки в конце
- Неизменяемость строк в Java
- Определите, является ли строка C допустимым int в C
Прежде чем приступить к дальнейшей суете незыблемости , давайте просто взглянем на class String
и его функциональные возможности, прежде чем прийти к какому-либо выводу.
Вот как работает String
:
String str = "knowledge";
Это, как обычно, создает строку, содержащую "knowledge"
и присваивает ей ссылку str
. Достаточно просто? Позволяет выполнить еще несколько функций:
String s = str; // assigns a new reference to the same string "knowledge"
Давайте посмотрим, как работает следующее выражение:
str = str.concat(" base");
Это добавляет строку " base"
к str
. Но подождите, как это возможно, поскольку объекты String
неизменяемы? Ну, к вашему удивлению, это так.
Когда выполняется вышеуказанный оператор, VM берет значение String str
, то есть "knowledge"
и добавляет " base"
, предоставляя нам значение "knowledge base"
. Теперь, поскольку String
s неизменяемы, VM не может присвоить это значение str
, поэтому он создает новый объект String
, дает ему значение "knowledge base"
и дает ему ссылку str
.
Здесь важно отметить, что, хотя объект String
неизменен, его ссылочной переменной нет. Вот почему в приведенном выше примере ссылка ссылалась на вновь образованный объект String
.
На этом этапе в приведенном выше примере у нас есть два объекта String
: первый, который мы создали со значением "knowledge"
, на которое указывает s
, и вторая "knowledge base"
, на которую указывает str
. Но, технически, у нас есть три объекта String
, третий – буквальная "base"
в выражении concat
.
Важные факты о использовании строк и памяти
Что, если бы у нас не было другой ссылки на "knowledge"
? Мы бы потеряли эту String
. Однако он все еще существовал бы, но считался бы утраченным из-за отсутствия ссылок. Посмотрите еще один пример ниже
String s1 = "java"; s1.concat(" rules"); System.out.println("s1 refers to "+s1); // Yes, s1 still refers to "java"
Что происходит:
- Первая строка довольно проста: создайте новую
String
"java"
и обратитесь к нейs1
. - Затем VM создает еще один новый
"java rules"
String
, но ничто не ссылается на него. Итак, втораяString
мгновенно теряется. Мы не можем достичь этого.
Контрольная переменная s1
прежнему относится к исходной String
"java"
.
Почти каждый метод, применяемый к объекту String
для его модификации, создает новый объект String
. Итак, куда идут эти объекты String
? Ну, они существуют в памяти, и одной из ключевых целей любого языка программирования является эффективное использование памяти.
Поскольку приложения растут, для String
литералов очень часто занимать большую площадь памяти, что может даже вызвать избыточность. Итак, чтобы сделать Java более эффективной, JVM выделяет специальную область памяти, называемую «String constant pool».
Когда компилятор видит String
литерал, он ищет String
в пуле. Если совпадение найдено, ссылка на новый литерал направлена на существующую String
и новый объект String
не создается. Существующая String
просто имеет еще одну ссылку. Здесь возникает смысл сделать объекты String
неизменяемыми:
В пуле констант String
объект String
может иметь одну или несколько ссылок. Если несколько ссылок указывают на ту же String
даже не зная ее, было бы плохо, если бы одна из ссылок изменила значение String
. Вот почему объекты String
неизменяемы.
Итак, теперь вы можете сказать, что, если кто-то переопределит функциональность classа String
? Вот почему class String
помечен как final
так что никто не может переопределить поведение его методов.
Строка неизменной означает, что вы не можете изменить сам объект, но вы можете изменить ссылку на объект.
Когда вы выполняете a = "ty"
, вы фактически меняете ссылку a
на новый объект, созданный строковым литералом "ty"
.
Изменение объекта означает использование его методов для изменения одного из его полей (или поля являются общедоступными, а не окончательными, так что они могут быть обновлены извне, без доступа к ним с помощью методов), например:
Foo x = new Foo("the field"); x.setField("a new field"); System.out.println(x.getField()); // prints "a new field"
Хотя в неизменяемом classе (объявленном как 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"
Вы меняете то, a
что ссылаетесь . Попробуй это:
String a="a"; System.out.println("a 1-->"+a); String b=a; a="ty"; System.out.println("a 2-->"+a); System.out.println("b -->"+b);
Вы увидите, что объект, к которому относится a
а затем b
, не изменился.
Если вы хотите, чтобы ваш код не изменял объект, a
ссылается объект, попробуйте:
final String a="a";
Строка – это char[]
содержащая ряд кодовых единиц UTF-16 , смещение int
в этот массив и длину int
.
Например.
String s
Он создает пространство для ссылки на строку. Назначение ссылок на копии, но не изменяет объекты, к которым относятся эти ссылки.
Вы также должны знать, что
new String(s)
не делает ничего полезного. Он просто создает другой экземпляр, поддерживаемый тем же массивом, смещением и длиной, что и s
. Для этого очень редко возникает причина, поэтому большинство программистов Java считают это плохой практикой.
Строки с двойными кавычками Java, такие как "my string"
, действительно являются ссылками на интернированные экземпляры String
поэтому "bar"
является ссылкой на тот же экземпляр String, независимо от того, сколько раз он появляется в вашем коде.
«Привет» создает один экземпляр, который объединяется, а new String(...)
создает экземпляр без объединения. Попробуйте System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello")));
и вы должны увидеть true,false,false
неизменяемое означает, что вы не можете изменить значение одного и того же реферала. Каждый раз, когда вы должны создавать новые ссылки, это означает новое местоположение памяти. например:
String str="abc"; str="bcd";
здесь, в приведенном выше коде, в памяти есть 2 блока для хранения значения. Первый для значения «abc» и второй для «bcd». Второе значение не заменяет первое значение.
это называется непреложным.
В вашем примере переменная a
является просто ссылкой на экземпляр строкового объекта. Когда вы говорите a = "ty"
, вы фактически не изменяете строковый объект, а скорее указываете ссылку на совершенно другой экземпляр classа string.
глянь сюда
class ImmutableStrings { public static void main(String[] args) { testmethod(); } private static void testmethod() { String a="a"; System.out.println("a 1-->"+a); System.out.println("a 1 address-->"+a.hashCode()); a = "ty"; System.out.println("a 2-->"+a); System.out.println("a 2 address-->"+a.hashCode()); } }
вывод:
a 1-->a a 1 address-->97 a 2-->ty a 2 address-->3717
Это означает, что всякий раз, когда вы изменяете содержимое неизменяемого строкового объекта, a
создан новый объект. т.е. вам не разрешено изменять содержимое неизменяемого объекта. поэтому адрес для обоих объектов отличается.
Вы не изменяете объект в инструкции присваивания, вы заменяете один неизменяемый объект на другой. String("a")
объекта String("a")
не изменяется на String("ty")
, она отбрасывается, а ссылка на ty
записывается в нее вместо нее.
Напротив, StringBuffer
представляет собой изменяемый объект. Вы можете сделать это:
StringBuffer b = new StringBuffer("Hello"); System.out.writeln(b); b.append(", world!"); System.out.writeln(b);
Здесь вы не повторно назначили b
: он все еще указывает на тот же объект, но содержимое этого объекта изменилось.
Фактически вы получаете ссылку на новую строку, сама строка не изменяется, так как она неизменна. Это актуально.
Видеть
Неизменяемые объекты в Википедии
Неизменяемым объектом является объект, состояние которого не может быть изменено после его создания.
Таким образом, a = "ABC"
<- неизменяемый объект. «a» содержит ссылку на объект. И, a = “DEF”
<- еще один неизменный объект, «a» теперь ссылается на него.
После назначения строкового объекта этот объект не может быть изменен в памяти.
В итоге, вы сделали, чтобы изменить ссылку «a» на новый строковый объект.
String S1="abc"; S1.concat("xyz"); System.out.println("S1 is", + S1); String S2=S1.concat("def"); System.out.println("S2 is", + S2);
Это показывает, что после создания строкового объекта его нельзя изменить. EveryTime вам нужно создать новое и поместить в другой String. S
Я думаю, что следующий код очищает разницу:
String A = new String("Venugopal"); String B = A; A = A +"mitul"; System.out.println("A is " + A); System.out.println("B is " + B); StringBuffer SA = new StringBuffer("Venugopal"); StringBuffer SB = SA; SA = SA.append("mitul"); System.out.println("SA is " + SA); System.out.println("SB is " + SB);
String
Java неизменна, String
сохранит значение в форме объекта. поэтому, если u присваивает значение String a="a";
он создаст объект, и значение будет сохранено в нем, и снова, если вы назначаете значение a="ty"
означает, что он создаст другой объект, сохраните значение в этом, если вы хотите четко понять, проверьте has code
для String
.
Изменяется только эта ссылка. Сначала a
ссылался на строку «a», а позже вы изменили ее на «ty». Строка «a» остается неизменной.
В вашем примере a
сначала ссылается на "a"
, а затем на "ty"
. Вы не мутируете какой-либо экземпляр String
; вы просто изменяете, к чему относится экземпляр String
. Например, это:
String a = "a"; String b = a; // b refers to the same String as a a = "b"; // a now refers to a different instance System.out.println(b);
печатает «a», потому что мы никогда не мутируем экземпляр String
который указывает b
.
Если в какой-либо bar
объектов содержится ссылка на изменяемый объект foo
и инкапсулирует часть своего состояния в изменчивые аспекты состояния foo
, это позволит коду, который может изменить эти аспекты foo
чтобы изменить соответствующие аспекты состояния bar
без фактического трогательный bar
или даже знать о его существовании . Как правило, это означает, что объекты, которые инкапсулируют свое собственное состояние с использованием изменяемых объектов, должны гарантировать, что никакие ссылки на эти объекты не будут подвержены какому-либо коду, который может неожиданно их изменить. В отличие от этого, если bar
содержит ссылку на объект moo
и использует только неизменяемые аспекты moo
отличные от идентификатора, чтобы инкапсулировать его состояние, тогда bar
может свободно выставлять moo
для внешнего кода, не беспокоясь о чем-либо, что может сделать внешний код.
Надеюсь, что приведенный ниже код прояснит ваши сомнения:
public static void testString() { String str = "Hello"; System.out.println("Before String Concat: "+str); str.concat("World"); System.out.println("After String Concat: "+str); StringBuffer sb = new StringBuffer("Hello"); System.out.println("Before StringBuffer Append: "+sb); sb.append("World"); System.out.println("After StringBuffer Append: "+sb); }
Перед String Concat: Привет
После String Concat: Hello
Перед добавлением StringBuffer: Hello
После добавления StringBuffer: HelloWorld
Вероятно, каждый ответ, приведенный выше, прав, но мой ответ специфичен для использования метода hashCode()
, чтобы доказать такие точки, как String … после его создания нельзя изменить, а изменения приведут к новому значению в разных ячейках памяти.
public class ImmutabilityTest { private String changingRef = "TEST_STRING"; public static void main(String a[]) { ImmutabilityTest dn = new ImmutabilityTest(); System.out.println("ChangingRef for TEST_STRING OLD : " + dn.changingRef.hashCode()); dn.changingRef = "NEW_TEST_STRING"; System.out.println("ChangingRef for NEW_TEST_STRING : " + dn.changingRef.hashCode()); dn.changingRef = "TEST_STRING"; System.out.println("ChangingRef for TEST_STRING BACK : " + dn.changingRef.hashCode()); dn.changingRef = "NEW_TEST_STRING"; System.out.println("ChangingRef for NEW_TEST_STRING BACK : " + dn.changingRef.hashCode()); String str = new String("STRING1"); System.out.println("String Class STRING1 : " + str.hashCode()); str = new String("STRING2"); System.out.println("String Class STRING2 : " + str.hashCode()); str = new String("STRING1"); System.out.println("String Class STRING1 BACK : " + str.hashCode()); str = new String("STRING2"); System.out.println("String Class STRING2 BACK : " + str.hashCode()); } }
ВЫВОД
ChangingRef for TEST_STRING OLD : 247540830 ChangingRef for NEW_TEST_STRING : 970356767 ChangingRef for TEST_STRING BACK : 247540830 ChangingRef for NEW_TEST_STRING BACK : 970356767 String Class STRING1 : -1163776448 String Class STRING2 : -1163776447 String Class STRING1 BACK : -1163776448 String Class STRING2 BACK : -1163776447
Строка неизменна, это означает, что содержимое объекта String не может быть изменено после его создания. Если вы хотите изменить содержимое, вы можете перейти на StringBuffer / StringBuilder вместо String. StringBuffer и StringBuilder являются изменяемыми classами.