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


219

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

String str = new String();

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

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

Теперь в Java объекты String неизменны. Тогда как же объекту strможет быть присвоено значение «Помощь!». Разве это не противоречит неизменности строк в Java? Кто-нибудь может объяснить мне точную концепцию неизменности?

Редактировать:

Хорошо. Теперь я понимаю, но только один дополнительный вопрос. Как насчет следующего кода:

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()метода?


str - ТОЛЬКО ссылка, а не сам объект
ahmednabil88

Я надеюсь, что это помогает pushkarrajpujari.com/article/strings-in-java
Пушкардж Пуджари

Ответы:


317

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


15
+1 за вымышленный метод, он демонстрирует разницу между неизменным объектом и другими.
Заппи

1
Спасибо gustafc за правильные примеры и четкое объяснение .... Но вы можете просто ответить на отредактированную часть вопроса, пожалуйста? Это прояснит мое понимание.
Light_handle

17
Я никогда не видел такой ответ раньше. Обсудили каждую мелочь.
Майкл 'Майк' Ардан

+1 Вот идея, неизменяемые объекты в java похожи на копии по значению, вы можете иметь 2 ссылки на строку, но вы должны рассматривать их как две отдельные строки, так как она неизменна, и работа с одной из них не повлияет на другое
Халед.К

1
Java - чем больше ты думаешь, что знаешь, тем меньше ты на самом деле знаешь.
Зеешан

24

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

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

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

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

final String STR = "Hello";

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


Но в этом случае объектом String является «str», который сначала содержит значение «Hello», а затем получает новое значение «Help!». Что именно вы подразумеваете под "объектами String, содержащими строки" Hello "и" Help! ", Нельзя изменить свои значения, следовательно, они неизменны". ??? Простите, если это глупый вопрос. Но я должен очистить это ...
Light_handle

2
Вы случайно не пытались программировать на С? Просто прочитайте учебник по указателям, и вы отлично поймете ответ Коберда.
Райан Фернандес

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

Вы путаете ссылки и объекты - strэто не «объект», это ссылка на объект. Если вы String str = "Hello";последовали String anotherReference = str;вам не 2 объекта String, у вас есть один объект (буквальная «Hello») и 2 ссылки на него ( strи anotherReference).
Nate

У меня нет прав на редактирование, но если бы я это сделал, я бы отредактировал 1-е предложение coobirds: «Ссылки str могут изменяться, но сами объекты String не могут».
j3App

10

Light_handle Я рекомендую вам прочитать « Размер чашки» - рассказ о переменных и Проходном значении, пожалуйста (Продолжение размера чашки) . Это очень поможет при чтении постов выше.

Читали ли вы их? Да. Хорошо.

String str = new String();

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

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

str --- > ""

str  = "Hello";

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

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

str -+   ""
     +-> "Hello"

str = "Help!";

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

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

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

"" И "Привет" собирать мусор?
Прабин Тимсина

@PrabinTimsina Это действительно новый вопрос. Ответ: stackoverflow.com/questions/15324143/…
Майкл Ллойд Ли mlk

9

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

String s1 = "hello";

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

String s2 = s1;

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

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

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

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

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

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

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

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

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

Поскольку String является неизменным, виртуальная машина Java не позволяет нам изменять строку s1 ее методом. Он создаст все новые объекты String в пуле следующим образом.

s1.concat(" overflow");

                 ___________________
                |                   |
s1.concat ----> |  stack overflow   |
                |___________________|

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

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

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

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

s1 = s1.concat(" overflow");

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

         ___________________
        |                   |
s1 ---->|  stack overflow   |
        |___________________|


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

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


6

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


5

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


5

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

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!

4

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


3

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

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


3

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

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

он создаст одну строку "Миссисипи" и добавит ее в пул строк, поэтому теперь 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) ПриветWorld


2

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

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

Если вы видите здесь, я использую concatметод для изменения исходной строки, то есть «New String» со строкой «Added String», но все же я получил вывод как и предыдущий, следовательно, это доказывает, что вы не можете изменить ссылку на объект класса 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

2

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

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

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

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).
  • Метод Replace возвращает один и тот же объект String, если оба параметра совпадают

0

Для тех, кто интересуется, как нарушить неизменность 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

0

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


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

0

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

  1. Когда вы вызвали новую строку ().
  2. Когда ты назначил "привет".
  3. Когда ты назначил «помощь!».

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

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


0

Неизменность Я могу сказать, что вы не можете изменить саму строку. Предположим, у вас есть строка x, значение которой равно «abc». Теперь вы не можете изменить строку, то есть вы не можете изменить любой символ (ы) в «abc».

Если вам нужно изменить какие-либо символы в строке, вы можете использовать массив символов и изменить его или использовать 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

0

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

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

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


0

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

0

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

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


0

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

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

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

public String replace(char oldChar, char newChar) 

Также еще один источник, который делает это поведение явным (из документации замены функции)

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

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

  • автор Ли Бойнтон
  • автор Артур ван Хофф
  • автор Мартин Буххольц
  • автор Ульф Зибис

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


0

Строка Object - методы сами по себе сделаны «неизменяемыми». Это действие не вызывает изменений: "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);

// Хеш-код строки Object не изменяется.


0

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

Вы можете иметь несколько строковых переменных-указателей для указания HELLO String.

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

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

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


0

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


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


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

например

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

поэтому у s2 будет "hel";


-1

Строка в Java в Immutable и Final означает, что ее нельзя изменить или изменить:

Случай 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" теряется в пуле без ссылки.


-1

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

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

-1
    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 вместо string может использоваться, чтобы избежать этого.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.