Строка неизменна. В чем же смысл?

Я написал следующий код на неизменяемых строках.

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 является неизменным ? Не могли бы вы прояснить эту тему для меня?

источник: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

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

Что происходит:

  1. Первая строка довольно проста: создайте новую String "java" и обратитесь к ней s1 .
  2. Затем 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ами.

  • Форматировать строку Go без печати?
  • Найти строку текста в элементе и обернуть вокруг него tags span
  • Как проверить, существует ли строка в файле с Bash?
  • Как разбить строку, разделенную запятой?
  • Как обрезать пробелы из строки Python?
  • Репликация массива по элементам в Matlab
  • Разделение строки Java удалено пустым значением
  • Java: String split (): Я хочу, чтобы он включал пустые строки в конце
  • Как использовать «.» Как разделитель с String.split () в java
  • Определите, является ли строка C допустимым int в C
  • Является ли строка Java действительно неизменной?
  • Interesting Posts

    Отладка WebSocket в Google Chrome

    Как получить доступ к переменной $ scope в консоли браузера, используя AngularJS?

    Как передать значение аргумента по умолчанию для члена экземпляра методу?

    Как Windows 7 решает, какой маршрут нужно предпринять, если существуют 2 подключения к интернет-источнику? (Например, беспроводной и Ethernet-маршрутизатор)

    Перестановка множества наборов измерительных столбцов (широкий формат) в отдельные столбцы (длинный формат)

    Найти ключ установленного и активированного экземпляра Adobe Acrobat Professional без использования сторонних инструментов

    Разница между webdriver.get () и webdriver.navigate ()

    Как создать представление моментального снимка для какого-либо проекта или streamа в ClearCase?

    Есть ли способы просматривать сайт, заблокированный моим провайдером?

    Существуют ли программы Windows для изменения размеров разделов?

    Как перенаправить внешний URL в Angular2?

    Как клиентские приложения знают, как использовать IPv4 или IPv6?

    изменение размера ImageIcon в JButton

    jackson и общий тип ссылки

    Подключение к SSH из-за прокси-сервера HTTP

    Давайте будем гением компьютера.