Почему следующий пример опровергает то, что строки являются неизменяемыми объектами в Java? - PullRequest
4 голосов
/ 15 августа 2011

Я использую Java-компилятор OpenJDK под Ubuntu. Я хотел преобразовать массив символов в строку, и когда это, казалось, привело к неоднозначным результатам, я попытался написать собственный метод toString. В процессе я написал тестовую программу, в которой (из забавы) я попытался скомпилировать следующий код.

class toString{
    public static void main(String[] args){
        string = "abc";
        string = string + "bcd";
        System.out.println(string);
    }
}

Теперь я знаю, что String объекты в Java являются неизменяемыми, и код фактически должен был выдавать ошибку, но, к моему удивлению, он выводил abcbcd на консоль. Означает ли это, что String объекты в Java являются изменяемыми или в этом случае что-то не так с реализацией компилятора OpenJDK?

Ответы [ 9 ]

20 голосов
/ 15 августа 2011

Код, который вы разместили выше, на самом деле не изменяет никакие строки, хотя выглядит так.Причина в том, что эта строка не изменяет строку:

string = string + "bcd";

Вместо этого она делает следующее:

  1. Создайте новую строку со значением string + "bcd".
  2. Измените, на какую строку ссылается string, чтобы ссылаться на эту новую строку.

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

Важная деталь, которая смущает многих новых Java-программистов, состоит в том, что приведенная выше строка часто пишется как

string += "bcd";

, который выглядит еще сильнее, как будто он объединяет bcd в конец строки и, таким образом, изменяет его, даже если он эквивалентен приведенному выше коду и, следовательно, не вызывает никаких изменений в реальном объекте String (опять же, он работает путем создания нового String объекта и изменения объекта, на который ссылается ссылка.)

Чтобы увидеть, что здесь происходит, вы фактически изменяете ссылку, а не строку, на которую она ссылаетсяк, вы можете попробовать переписать код, чтобы сделать string final, который не позволяет вам изменять, на какой объект ссылаются.Если вы сделаете это, вы обнаружите, что код больше не компилируется.Например:

class toString{
    public static void main(String[] args){
        final String string = "abc";
        string = string + "bcd";    // Error: can't change string!
        System.out.println(string);
    }
}

Последнее замечание. Еще одна распространенная причина скорби для новых Java-программистов при использовании String s заключается в том, что String имеет методы, которые выглядят как видоизменяющие строку, но в действительности их нет.Например, этот код не работает правильно:

String s = "HELLO, WORLD!";
s.toLowerCase(); // Legal but incorrect
System.out.println(s); // Prints HELLO, WORLD!

Здесь вызов s.toLowerCase() фактически не преобразует символы строки в нижний регистр, а вместо этого создает новую строку с набором символовв нижнем регистре.Если затем переписать код как

String s = "HELLO, WORLD!";
s = s.toLowerCase();   // Legal and correct
System.out.println(s); // Prints hello, world!

, тогда код будет работать правильно.Опять же, ключевая деталь здесь в том, что присвоение s не меняет какой-либо конкретный объект String, а просто настраивает, к какому объекту относится s.

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

5 голосов
/ 15 августа 2011

Нет, нет ошибки - вы не меняете содержимое какого-либо строкового объекта.

Вы изменяете значение строки переменной , которая совершенно отличается. Посмотрите на это как на две операции:

  • Создание новой строки, результат выражения string + "bcd"
  • Присвоение ссылки на новую строку обратно переменной string

Давайте выделим их явно:

String string = "abc";
String other = string + "bcd";

// abc - neither the value of string nor the object's contents have changed
System.out.println(string); 

// This is *just* changing the value of the string variable. It's not making
// any changes to the data within any objects.
string = other;

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

2 голосов
/ 15 августа 2011

Разница между ссылками на объект и самим объектом.

String XXX = "xxx";

Средства: Создайте новую переменную и назначьте ссылку на экземпляр объекта String, который содержит буквенную строку «xxx».

XXX = XXX + "yyy";

Средства:

Получить ссылку на имеющийся у нас объект в переменной с именем XXX. Создайте новый объект типа String, который содержит строковый литерал "yyy". Добавьте их вместе, выполнив строку + оператор. Эта операция создаст новый объект String, содержащий буквальную строку «xxxyyy». После всего этого мы снова помещаем ссылку на новый объект в переменную XXX.

Старый ссылочный объект, содержащий "xxx", больше не используется, но его содержимое никогда не изменялось.

В качестве встречного доказательства есть пример:

String a = "abc";
String b = "def";
String c = a;

a = a + b;

System.out.println(a); // will print "abcdef".
System.out.println(b); // will print "def".
System.out.println(c); // will print "abc".

// Now we compare references, in java == operator compare references, not the content of objects.

System.out.println(a == a); // Will print true
System.out.println(a == c); // Will print false, objects are not the same!

a = c;

System.out.println(a == c); // Will print true, now a and b points on the same instance.

Экземпляр объекта - это нечто «абстрактное», которое живет в части памяти вашей программы. Ссылочная переменная - это ссылка на эту часть памяти. Вы можете получить доступ к объектам только через переменные (или возвращаемые значения).

Один объект может иметь более одной переменной, указывающей на него.

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

1 голос
/ 15 августа 2011

Это не опровергает это.На самом деле он не скомпилируется, так как string не объявлен как объект String.Но, допустим, вы имели в виду:

class toString{
    public static void main(String[] args){
        String string = "abc";
        string = string + "bcd";
        System.out.println(string);
    }
}

Посмотрите, как оператор + создает новую строку, оставляя "abc" в такте.Оригинальный «abc» все еще существует, но все, что вы на самом деле сделали, - это создайте новую строку «abcbcd» и перезапишите исходную ссылку на «abc» при выполнении: string = string + "bcd".Если вы изменили этот код на этот, вы поймете, что я имею в виду:

class toString {
    public static void main(String[] args ) {
        String originalString = "abc";
        String newString = originalString + "bcd";

        System.out.println( originalString );  // prints the original "abc";
        System.out.println( newString );       // prints the new string "abcbcd";
    }
}
0 голосов
/ 15 августа 2011

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

Если мы сделаем код скомпилированным и слегка его изменим:

class toString{
    public static void main(String[] args){
        String string = "abc";
        System.out.println(string);
        string = string + "bcd";
        System.out.println(string);
    }
}

Вы увидите «abc», а затем «abcbcd», что может заставить вас думать, что строка изменилась, но не изменилась.

Когда вы делаете строку = / * что угодно * /, вы перезаписываете то, что раньше было в переменной, называемой строкой, с новым значением.

Если бы в String был метод, такой как setCharAt (int index, char value), он был бы изменяемым.

0 голосов
/ 15 августа 2011

Переменная String string сама является изменяемой.

Объект String "abc" является неизменным, как и объект String "bcd", а результат конкатенации "abcbcd".Этот последний результат присваивается переменной.

Строка не была видоизменена при выполнении этого фрагмента кода.

0 голосов
/ 15 августа 2011

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

0 голосов
/ 15 августа 2011

string = string + "bcd" устанавливает новый экземпляр String для переменной string, а не изменяет этот объект

0 голосов
/ 15 августа 2011

String объекты являются неизменными, вы просто переназначаете значение от string до string + "bcd" в вашем примере кода.Вы не изменяете существующий объект String, вы создаете новый и присваиваете его старому имени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...