Вопрос-ответ

String is immutable. What exactly is the meaning? [duplicate]

Строка неизменяема. Что именно это означает?

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

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-->a  
a 2-->ty

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

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

Переведено автоматически
Ответ 1

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

Когда выполняется приведенный выше оператор, виртуальная машина принимает значение String str, т.Е. "knowledge" и добавляет " base", давая нам значение "knowledge base". Теперь, поскольку Strings неизменяемы, виртуальная машина не может присвоить это значение str, поэтому она создает новый String объект, присваивает ему значение "knowledge base" и дает на него ссылку str.

Здесь важно отметить, что, хотя String объект неизменяем, его ссылочная переменная - нет. Итак, вот почему в приведенном выше примере ссылка была сделана для обозначения вновь созданного String объекта.

На данный момент в приведенном выше примере у нас есть два String объекта: первый, который мы создали со значением "knowledge", на которое указывает s, и второй, на который указывает "knowledge base" str. Но, технически, у нас есть три String объекта, третий из которых является литералом "base" в concat инструкции.

Важные факты об использовании строки и памяти

Что, если бы у нас не было другой ссылки s на "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. Затем виртуальная машина создает еще одну новую String "java rules", но на нее ничего не ссылается. Итак, вторая String мгновенно теряется. Мы не можем до нее добраться.

Ссылочная переменная s1 по-прежнему ссылается на исходную String "java".

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

По мере роста приложений очень часто String литералы занимают большую область памяти, что может даже привести к избыточности. Итак, чтобы сделать Java более эффективной, JVM выделяет специальную область памяти, называемую "Пулом строковых констант".

Когда компилятор видит String литерал, он ищет String в пуле. Если найдено совпадение, ссылка на новый литерал направляется на существующий String и новый String объект не создается. У существующего String просто есть еще одна ссылка. Здесь возникает вопрос о том, как сделать String объекты неизменяемыми:

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

Ну, теперь вы могли бы сказать, что, если кто-то переопределит функциональность String класса? Именно по этой причине String класс помечен final, чтобы никто не мог переопределить поведение его методов.

Ответ 2

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

При выполнении 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, чтобы предотвратить модификацию через наследование) (его методы не могут изменять его поля, а также поля всегда являются закрытыми и рекомендуется быть 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"
Ответ 3

Вы меняете то, на что 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 and then b, не изменился.

Если вы хотите, чтобы ваш код не изменял, на какой объект a ссылается, попробуйте:

final String a="a";
Ответ 4

Строка - это char[] содержащая последовательность единиц кода UTF-16, int смещение в этом массиве и int длину.

Например.

String s

Это создает пространство для ссылки на строку. Присвоение копирует ссылки по кругу, но не изменяет объекты, на которые ссылаются эти ссылки.

Вы также должны знать, что

new String(s)

на самом деле ничего полезного не делает. Он просто создает другой экземпляр, поддерживаемый тем же массивом, смещением и длиной, что и s. Для этого очень редко есть причина, поэтому большинство Java-программистов считают это плохой практикой.

Строки Java, заключенные в двойные кавычки, такие как "my string", на самом деле являются ссылками на интернированные String экземпляры, поэтому "bar" это ссылка на один и тот же экземпляр String, независимо от того, сколько раз он появляется в вашем коде.


"Hello" создает один экземпляр, который является объединенным, а new String(...) создает экземпляр, не являющийся объединенным. Попробуйте System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello")));, и вы должны увидеть true,false,false

java string