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

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

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 неизменяемы? К вашему удивлению, это так.

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

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

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

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

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

Контрольная переменная s1 по-прежнему относится к оригиналу String "java".

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

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

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

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

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

Ответ 2

Строка неизменна, означает, что вы не можете изменить сам объект, но вы можете изменить ссылку на объект. Когда вы вызываете 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, но вы можете вернуть новую строку, т.е.

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 и затем b, не изменился.

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

final String a="a";

Ответ 4

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

Например.

String s

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

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

new String(s)

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

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


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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

Вы не меняете объект в инструкции присваивания, вы заменяете один неизменяемый объект на другой. Объект String("a") не изменяется на String("ty"), он отбрасывается, а ссылка на ty записывается в a вместо него.

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

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

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

Ответ 9

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

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

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

В общем, вы сделали, чтобы изменить ссылку "a" на новый строковый объект.

Ответ 10

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

Ответ 11

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

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

Ответ 12

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

Ответ 13

см. здесь

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

Ответ 14

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

Ответ 15

В вашем примере a сначала относится к "a", а затем к "ty". Вы не мутируете какой-либо экземпляр String; вы просто изменяете, к которому относится String instance 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);

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

Ответ 16

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

Ответ 17

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

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: Hello
После String Concat: Hello
Перед добавлением StringBuffer: Hello После добавления StringBuffer: HelloWorld

Ответ 18

Строка неизменна, означает, что содержимое объекта String не может быть изменено

если вы хотите изменить содержимое, используйте StringBuffer вместо String, который изменен

Ответ 19

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

    }
}

OUTPUT

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