Неизменяемость строк в Java

Рассмотрим следующий пример.

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

Теперь в Java объекты String неизменяемы. Тогда как объекту str может быть присвоено значение "Справка!". Разве это не противоречит неизменности строк в Java? Может ли кто-нибудь объяснить мне точное понятие неизменности?

Edit:

Ok. Теперь я получаю это, но только один вопрос. Как насчет следующего кода:

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

Означает ли это, что два объекта снова создаются ( "Миссисипи" и "M! ss! ss! pp!" ), а ссылка str указывает на другой объект после метода replace()?

Ответ 1

str не является объектом, это ссылка на объект. "Hello" и "Help!" - два разных объекта String. Таким образом, str указывает на строку. Вы можете изменить то, на что оно указывает, но не то, на что оно указывает.

Возьмите этот код, например:

String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"

Теперь нет ничего 1 которое мы могли бы сделать для s1, что повлияет на значение s2. Они относятся к одному и тому же объекту - строке "Hello" - но этот объект является неизменным и, следовательно, не может быть изменен.

Если мы сделаем что-то вроде этого:

s1 = "Help!";
System.out.println(s2); // still prints "Hello"

Здесь мы видим разницу между мутацией объекта и изменением ссылки. s2 все еще указывает на тот же объект, на который мы первоначально установили s1, чтобы указать на. Установка s1 в "Help!" изменяет только ссылку, а объект String, который он изначально указывал, остается неизменным.

Если строки были изменчивыми, мы могли бы сделать что-то вроде этого:

String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"

Изменить для ответа на редактирование OP:

Если вы посмотрите исходный код для String.replace(char, char) (также доступен в src.zip в вашем JDK каталог установки - советник должен смотреть там, когда вы задаетесь вопросом, как что-то действительно работает), вы можете видеть, что он делает следующее:

  • Если в текущей строке есть одно или несколько вхождений oldChar, создайте копию текущей строки, где все вхождения oldChar заменяются на newChar.
  • Если oldChar отсутствует в текущей строке, верните текущую строку.

Итак, да, "Mississippi".replace('i', '!') создает новый объект String. Опять же, имеет место следующее:

String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects

Теперь ваша домашняя работа - посмотреть, что делает этот код, если вы меняете s1 = s1.replace('i', '!'); на s1 = s1.replace('Q', '!');:)


1 Собственно, можно мутировать строки (и другие неизменные объекты). Это требует отражения и очень, очень опасно и никогда не должно использоваться, если вы действительно не заинтересованы в уничтожении программы.

Ответ 2

Объект, с которым могут ссылаться ссылки str, но сами объекты String не могут.

Объекты String, содержащие строку "Hello" и "Help!", не могут изменять свои значения, поэтому они являются неизменяемыми.

Неизбежность объектов String не означает, что ссылки, указывающие на объект, не могут измениться.

Один из способов предотвратить изменение ссылки str состоит в том, чтобы объявить ее как final:

final String STR = "Hello";

Теперь при попытке назначить другой String to str будет возникать ошибка компиляции.

Ответ 3

Light_handle Я рекомендую вам прочитать Размер чашки - рассказ об переменных и Pass-by-Value Please (размер кубка продолжен). Это очень поможет при чтении сообщений выше.

Вы читали их? Да. Хорошо.

String str = new String();

Это создает новый "пульт дистанционного управления" под названием "str" и устанавливает его в значение new String() (или "").

например. в памяти это создает:

str --- > ""

str  = "Hello";

Затем изменяется пульт дистанционного управления "str" , но не изменяется исходная строка "".

например. в памяти это создает:

str -+   ""
     +-> "Hello"

str = "Help!";

Затем изменяется пульт дистанционного управления "str" , но не изменяется исходная строка "" или объект, на который в данный момент указывает удаленный элемент.

например. в памяти это создает:

str -+   ""
     |   "Hello"
     +-> "Help!"

Ответ 4

Позволяет разбить его на несколько частей

String s1 = "hello";

Это выражение создает строку, содержащую hello и занимая пространство в памяти, т.е. в Constant String Pool, и назначает его ссылочному объекту s1

String s2 = s1;

Этот оператор присваивает той же строке hello новую ссылку s2

         __________
        |          |
s1 ---->|  hello   |<----- s2
        |__________| 

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

out.println(s1);    // o/p: hello
out.println(s2);    // o/p: hello

Хотя Строка неизменна, назначение может быть возможным, поэтому s1 теперь будет ссылаться на новое значение стек.

s1 = "stack";    
         __________
        |          |
s1 ---->|  stack   |
        |__________|

Но как насчет объекта s2, который указывает на hello, он будет таким, как есть.

         __________
        |          |
s2 ---->|  hello   |
        |__________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello

Так как String неизменен Java Virtual Machine не позволит нам изменить строку s1 по его методу. Он создаст все новые объекты String в пуле следующим образом.

s1.concat(" overflow");

                 ___________________
                |                   |
s1.concat ----> |  Qaru   |
                |___________________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello
out.println(s1.concat); // o/p: stack overflow

Обратите внимание, что если String будет изменяться, то выход был бы

out.println(s1);    // o/p: stack overflow

Теперь вы можете быть удивлены, почему String имеет такие методы, как concat() для изменения. Следующий фрагмент очистит ваше замешательство.

s1 = s1.concat(" overflow");

Здесь мы назначаем модифицированное значение строки обратно в ссылку s1.

         ___________________
        |                   |
s1 ---->|  Qaru   |
        |___________________|


out.println(s1);    // o/p: stack overflow
out.println(s2);    // o/p: hello

Итак, почему Java решила String стать последним классом. В противном случае любой может изменить и изменить значение строки. Надеюсь, это поможет немного.

Ответ 5

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

Ответ 6

Строка не изменится, ссылка на нее будет. Вы смешиваете неизменность с понятием полей final. Если поле объявлено как final, после его назначения его нельзя переназначить.

Ответ 7

Что касается замены части вашего вопроса, попробуйте следующее:

String str = "Mississippi"; 
System.out.println(str); //Prints Mississippi 

String other = str.replace("i", "!"); 
System.out.println(str); //still prints Mississippi 
System.out.println(other);  // prints M!ss!ss!pp!

Ответ 8

Хотя java пытается игнорировать его, str является не чем иным, как указателем. Это означает, что при первой записи str = "Hello"; вы создаете объект, на который указывает str. Когда вы переназначаете str, написав str = "Help!";, создается новый объект, и старый объект "Hello" получает мусор, собранный всякий раз, когда ему нравится java.

Ответ 9

Неизбежность подразумевает, что значение объекта-объекта не может измениться, вы никогда не сможете превратить "Hello" в "Help!".

Переменная str является ссылкой на объект, когда вы назначаете новое значение str, вы не изменяете значение объекта, к которому он ссылается, вы ссылаетесь на другой объект.

Ответ 10

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

String str = "Mississippi";  
System.out.println(str); // prints Mississippi 

он создаст одну строку "Миссисипи" и добавит ее в String pool поэтому теперь str указывает на Миссисипи.

str = str.replace("i", "!");  
System.out.println(str); // prints M!ss!ss!pp! 

Но после операции выше, одна строка будет создана "M! ss! ss! pp!" и он будет добавлен в пул строк. а также теперь str указывает на M! ss! ss! pp!, а не на Миссисипи.

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

Давайте еще один пример

String s1 = "Hello"; 
String s2 = "World"; 
String s = s1 + s2;

это выше трех строк добавит три объекта строки в пул строк.
1) Привет,
2) Мир
3) HelloWorld

Ответ 11

Использование:

String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String

Если вы видите здесь, я использую метод concat для изменения исходной строки, то есть "Новая строка" со строкой "Добавленная строка", но все же я получил результат как предыдущий, следовательно, это доказывает, что вы можете не измените ссылку на объект класса String, но если вы сделаете это по классу StringBuilder, он будет работать. Он указан ниже.

StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String

Ответ 12

Как сказал Линус Тольвард:

Обсуждение дешево. Покажите мне код

Взгляните на это:

public class Test{
    public static void main(String[] args){

        String a = "Mississippi";
        String b = "Mississippi";//String immutable property (same chars sequence), then same object

        String c = a.replace('i','I').replace('I','i');//This method creates a new String, then new object
        String d = b.replace('i','I').replace('I','i');//At this moment we have 3 String objects, a/b, c and d

        String e = a.replace('i','i');//If the arguments are the same, the object is not affected, then returns same object

        System.out.println( "a==b? " + (a==b) ); // Prints true, they are pointing to the same String object

        System.out.println( "a: " + a );
        System.out.println( "b: " + b );

        System.out.println( "c==d? " + (c==d) ); // Prints false, a new object was created on each one

        System.out.println( "c: " + c ); // Even the sequence of chars are the same, the object is different
        System.out.println( "d: " + d );

        System.out.println( "a==e? " + (a==e) ); // Same object, immutable property
    }
}

Выходной сигнал

a==b? true
a: Mississippi
b: Mississippi
c==d? false
c: Mississippi
d: Mississippi
a==e? true

Итак, запомните две вещи:

  • Строки являются неизменяемыми до тех пор, пока вы не примените метод, который манипулирует и не создает новый (c и d).
  • Метод замены возвращает тот же объект String, если оба параметра одинаковы

Ответ 13

Для тех, кто задается вопросом, как разбить непрерывность String в Java...

код

import java.lang.reflect.Field;

public class StringImmutability {
    public static void main(String[] args) {
        String str1 = "I am immutable";
        String str2 = str1;

        try {
            Class str1Class = str1.getClass();
            Field str1Field = str1Class.getDeclaredField("value");

            str1Field.setAccessible(true);
            char[] valueChars = (char[]) str1Field.get(str1);

            valueChars[5] = ' ';
            valueChars[6] = ' ';

            System.out.println(str1 == str2);
            System.out.println(str1);
            System.out.println(str2);           
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

Выход

true
I am   mutable
I am   mutable

Ответ 14

В Java объекты обычно получают доступ по ссылкам. В вашем фрагменте кода str является ссылкой, которая сначала присваивается "Hello" (автоматически созданный объект или получен из постоянный пул), а затем вы назначил другой объект "Помощь!" к той же ссылке. Следует отметить, что ссылка является той же и измененной, но объекты разные. Еще одна вещь в вашем коде вы получили доступ к трем объектам,

  • Когда вы вызываете новую строку String().
  • Когда вы назначили "привет".
  • Когда вы назначили "help!".

Вызов новой String() создает новый объект, даже если он существует в пуле строк, поэтому обычно его не следует использовать. Чтобы поместить строку, созданную из новой строки String() в пул строк, вы можете попробовать метод intern().

Надеюсь, это поможет.

Ответ 15

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


String a = "a";
System.out.println("String a is referencing to "+a); // Output: a

a.concat("b");
System.out.println("String a is referencing to "+a); // Output: a

a = a.concat("b");
System.out.println("String a has created a new reference and is now referencing to "+a); // Output: ab

Ответ 16

Неизбежность, я могу сказать, что вы не можете изменить сам String. Предположим, что у вас есть String x, значение которого равно "abc". Теперь вы не можете изменить строку, т.е. Вы не можете изменить символ/с в "abc".

Если вам нужно изменить любой символ /s в String, вы можете использовать массив символов и изменить его или использовать StringBuilder.

String x = "abc";
x = "pot";
x = x + "hj";
x = x.substring(3);
System.out.println(x);

char x1[] = x.toCharArray();
x1[0] = 's';
String y = new String(x1);
System.out.println(y);

Вывод:

hj
sj

Ответ 17

Или вы можете попробовать:

public class Tester
{
public static void main(String[] args)
{
 String str = "Mississippi"; 
 System.out.println(str); // prints Mississippi 
 System.out.println(str.hashCode());

 str = str.replace("i", "!"); 
 System.out.println(str); // prints M!ss!ss!pp! 
 System.out.println(str.hashCode());
 }
 }

Это покажет, как изменяется хэш-код.

Ответ 18

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

Ответ 19

Здесь неизменяемость означает, что экземпляр может указывать на другую ссылку, но исходное содержимое строки не будет изменено в исходной ссылке. Позвольте мне объяснить в первом примере, данном вами. Первая str указывает на "Hello", ее Ok до этого. Второй раз он указывает на "Помогите!". Здесь str начал указывать на "Помощь!" и ссылка на строку "Hello" теряется, и мы не можем вернуть ее.

Фактически, когда str попытается изменить существующее содержимое, будет создана новая новая строка, и str начнет указывать на эту ссылку. Таким образом, мы видим, что строка в исходной ссылке не изменена, но это безопасно по ее ссылке, и экземпляр объекта, который начинается с ссылки, поэтому сохраняется неизменность.

Ответ 20

Супер опоздал на ответ, но хотел поместить краткое сообщение от автора класса String в Java

Строки постоянны; их значения не могут быть изменены после того, как они создано. Буферы String поддерживают изменяемые строки. Потому что String объекты неизменяемы, они могут использоваться совместно.

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

public String replace(char oldChar, char newChar) 

Также есть еще один источник, который делает это поведение явным (From replace function documentation)

Возвращает новую строку в результате замены всех вхождений oldChar в этой строке с помощью newChar.

Источник: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replace(char,%20char)

  • автор Ли Бойнтон
  • автор Артур ван Хофф
  • автор Мартин Буххольц
  • автор Ulf Zibis

Источник: JavaDoc строки.

Ответ 21

Строка "Объект" - сама процедура сделана "неизменной". Это действие не производит никаких изменений: "letters.replace(" bbb "," aaa ");

Но назначение данных приводит к изменению изменений в содержимом строк:

    letters = "aaa";
    letters=null;
    System.out.println(letters);
    System.out.println(oB.hashCode());
    System.out.println(letters);
    letters = "bbbaaa";
    System.out.println(oB.hashCode());
    System.out.println(letters);

//Хэш-код строки Объект не изменяется.

Ответ 22

Если HELLO является вашей строкой, вы не можете изменить HELLO на HILLO. Это свойство называется непреложным свойством.

У вас может быть несколько указателей String, чтобы указать HELLO String.

Но если HELLO является char Array, вы можете изменить HELLO на HILLO. Например,

char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this

Языки программирования имеют неизменяемые переменные данных, поэтому их можно использовать в качестве ключей в паре ключей, значений.

Ответ 23

Я бы объяснил это простым примером


рассмотрим любой массив символов: например, char a [] = {'h', 'e', 'l', 'l', 'o'}; и строка: Строка s = "привет";


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

например,

***String s="hello";
String s2=s.substrig(0,3);***

так что у s2 будет "hel";

Ответ 24

Строка в Java в неизменяемом и финальном просто означает, что ее нельзя изменить или изменить:

Случай 1:

class TestClass{  
 public static void main(String args[]){  
   String str = "ABC";  
   str.concat("DEF");  
   System.out.println(str);  
 }  
} 

Выход: ABC

Причина: ссылка на объект str не изменяется, фактически, новый объект Создается "DEF", который находится в пуле и не имеет ссылки вообще (т.е. потеряно).

Случай 2:

class TestClass{  
 public static void main(String args[]){  
   String str="ABC";  
   str=str.concat("DEF");  
   System.out.println(str);  
 }  
}  

Выход: ABCDEF

Причина: В этом случае str теперь ссылается на новый объект "ABCDEF", поэтому он печатает ABCDEF, то есть предыдущий объект str "ABC" теряется в пуле без ссылки.

Ответ 25

Поскольку String является неизменной, поэтому изменения не будут происходить, если вы не будете назначать возвращаемое значение функции string.so в вопросе присваивать значение возвращаемой переменной функции swap.

s = swap (s, n1, n2), тогда значение строки s изменится.

Я также получал неизменное значение, когда писал программу, чтобы получить строку перестановок (хотя она не дает всех перестановок, но это, например, ответ на ваш вопрос)

Вот пример.

> import java.io.*;  public class MyString { public static void
> main(String []args)throws IOException {  BufferedReader br=new
> BufferedReader(new InputStreamReader(System.in));  String
> s=br.readLine().trim(); int n=0;int k=0;  while(n!=s.length()) {
> while(k<n){  swap(s,k,n); System.out.println(s); swap(s,k,n); k++; }
> n++; } }  public static void swap(String s,int n1,int n2) { char temp;
> temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s);
> sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString();
> } }

но я не получал перестановочные значения строки из кода выше. Поэтому я назначил возвращаемое значение функции свопинга в строку и получил измененные значения строки. после присвоения возвращаемого значения я получил переменные значения строки.

/import java.util.*; import java.io.*; public class MyString { public static void main(String []args)throws IOException{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); 
String s=br.readLine().trim(); int n=0;int k=0; 
while(n!=s.length()){ while(k<n){ s=swap(s,k,n); 
System.out.println(s); s=swap(s,k,n); k++; } n++; } } 
public static String swap(String s,int n1,int n2){
char temp; temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); return s; } }

Ответ 26

    public final class String_Test {

    String name;
    List<String> list=new ArrayList<String>();

    public static void main(String[] args) {

        String_Test obj=new String_Test();
        obj.list.add("item");//List will point to a memory unit- i.e will have one Hashcode value #1234

        List<String> list2=obj.list; //lis1 also will point to same #1234

        obj.list.add("new item");//Hashcode of list is not altered- List is mutable, so reference remains same, only value in that memory location changes

        String name2=obj.name="Myname"; // name2 and name will point to same instance of string -Hashcode #5678
        obj.name = "second name";// String is Immutable- New String HAI is created and name will point to this new instance- bcoz of this Hashcode changes here #0089

        System.out.println(obj.list.hashCode());
        System.out.println(list2.hashCode());
        System.out.println(list3.hashCode());

        System.out.println("===========");
        System.out.println(obj.name.hashCode());
        System.out.println(name2.hashCode());
    }
}

Буду выкладывать ставить что то подобное

1419358369 1419358369

103056 65078777

Цель неизменяемого объекта состоит в том, что его значение не должно изменяться после назначения. Он будет возвращать новый объект каждый раз, когда вы пытаетесь изменить его в зависимости от реализации. Примечание: Stringbuffer вместо строки может быть использован, чтобы избежать этого.

К вашему последнему вопросу :: у вас будет одна ссылка и 2 строки в пуле строк. За исключением ссылки будет указывать на m! Ss! Ss! Pp!