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

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

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

19 ответов

Решение

Прежде чем продолжить суету неизменности, давайте просто посмотрим на 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", Теперь, так как String s являются неизменными, VM не может присвоить это значение 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 так что никто не может переопределить поведение его методов.

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

Когда вы выполняете a = "ty"вы фактически меняете ссылку на a к новому объекту, созданному литералом String "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"

Вы меняете то, что 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

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

String str="abc";
str="bcd";

здесь, в приведенном выше коде, в памяти есть 2 блока для хранения значения. первый для значения "abc" и второй для "bcd". второе значение не заменяет первое значение.

это называется неизменным.

В вашем примере переменная a это просто ссылка на экземпляр строкового объекта. Когда ты сказал a = "ty"вы фактически не изменяете строковый объект, а скорее указываете ссылку на совершенно другой экземпляр строкового класса.

Посмотреть здесь

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("ty"), он отбрасывается, и ссылка на ty записывается в a вместо него.

По сравнению, StringBuffer представляет собой изменяемый объект. Вы можете сделать это:

StringBuffer b = new StringBuffer("Hello");
System.out.writeln(b);
b.append(", world!");
System.out.writeln(b);

Здесь вы не переназначить b: он по-прежнему указывает на тот же объект, но содержимое этого объекта изменилось.

Неизменяемый объект - это объект, состояние которого нельзя изменить после его создания.

Так a = "ABC" <- неизменный объект. "а" содержит ссылку на объект. А также, a = "DEF" <- другой неизменный объект, "а" теперь содержит ссылку на него.

После назначения строкового объекта этот объект не может быть изменен в памяти.

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

Джава String неизменен, String Сохранит значение в виде объекта. так что если вы присваиваете значение String a="a"; это создаст объект, и значение будет сохранено в этом и снова, если вы присваиваете значение a="ty" означает, что это создаст другой объект, хранящий значение в этом, если вы хотите ясно понять, проверьте has code для String,

Я думаю, что следующий код устраняет разницу:

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 является неизменным, это означает, что содержимое объекта String не может быть изменено после его создания. Если вы хотите изменить содержимое, вы можете использовать StringBuffer/StringBuilder вместо String. StringBuffer и StringBuilder являются изменяемыми классами.

Надеюсь, что приведенный ниже код прояснит ваши сомнения:

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);
}

Перед строкой Concat: Привет
After String Concat: Привет
Перед StringBuffer Добавить: Привет
После StringBuffer Добавить: HelloWorld

В вашем примере a относится в первую очередь к "a"и затем "ty", Ты не мутируешь String пример; ты просто меняешь что String пример a относится к. Например, это:

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);

печатает "а", потому что мы никогда не мутируем String пример того, что b указывает на.

Только ссылка меняется. Первый a ссылался на строку "а", а позже вы изменили ее на "ты". Строка "а" остается прежней.

Возможно, каждый ответ, приведенный выше, является правильным, но мой ответ является специфическим для использования 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

Если какой-то объект bar содержит ссылку на изменяемый объект foo и заключает в себе некоторые его состояния в изменчивых аспектах foo Государство, которое позволит код, который может изменить эти аспекты foo изменить соответствующие аспекты bar состояние, не касаясь bar или даже зная о его существовании. Как правило, это означает, что объекты, которые инкапсулируют свое собственное состояние с использованием изменяемых объектов, должны обеспечивать, чтобы никакие ссылки на эти объекты не подвергались воздействию какого-либо кода, который мог бы неожиданно изменить их. В отличие от этого, если bar содержит ссылку на объект moo и использует только неизменные аспекты moo кроме идентификации, чтобы инкапсулировать свое состояние, то bar может свободно выставлять moo с внешним кодом, не беспокоясь о том, что внешний код может с ним сделать.

Другие вопросы по тегам