Inmutabilidad de cadenas en Java

Considera el siguiente ejemplo.

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

Ahora, en Java, los objetos String son inmutables. Entonces, ¿cómo puede asignarse al objeto str valor “Ayuda”? ¿No está contradiciendo esto la inmutabilidad de las cadenas en Java? ¿Alguien puede explicarme el concepto exacto de inmutabilidad?

Editar:

De acuerdo. Ahora lo estoy obteniendo, pero solo una pregunta de seguimiento. ¿Qué pasa con el siguiente código?

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

¿Esto significa que se crean dos objetos nuevamente (“Mississippi” y “M! Ss! Ss! Pp!”) Y la referencia str apunta a un objeto diferente después del método replace() .

str no es un objeto, es una referencia a un objeto. "Hello" y "Help!" son dos objetos String distintos Por lo tanto, str apunta a una cadena. Puedes cambiar lo que apunta , pero no aquello a lo que apunta .

Tome este código, por ejemplo:

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

Ahora, no hay nada que podamos hacer con s1 que afecte el valor de s2 . Se refieren al mismo objeto, la cadena "Hello" , pero ese objeto es inmutable y, por lo tanto, no se puede modificar.

Si hacemos algo como esto:

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

Aquí vemos la diferencia entre mutar un objeto y cambiar una referencia. s2 aún apunta al mismo objeto que inicialmente configuramos s1 para señalar. Configurando s1 en "Help!" solo cambia la referencia , mientras que el objeto String que se refirió originalmente permanece sin cambios.

Si las cadenas fueran mutables, podríamos hacer algo como esto:

 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" 

Editar para responder a la edición de OP:

Si observas el código fuente de String.replace (char, char) (también disponible en src.zip en el directorio de instalación de JDK, un consejo profesional es que veas allí siempre que te preguntes cómo funciona realmente algo) puedes ver que lo que hace es lo siguiente:

  • Si hay una o más apariciones de oldChar en la cadena actual, haga una copia de la cadena actual donde todas las ocurrencias de oldChar se reemplazan con newChar .
  • Si oldChar no está presente en la cadena actual, devuelve la cadena actual.

Entonces, sí, "Mississippi".replace('i', '!') Crea un nuevo objeto String . Nuevamente, lo siguiente es válido:

 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 

Tu tarea por ahora es ver lo que hace el código anterior si cambias s1 = s1.replace('i', '!'); a s1 = s1.replace('Q', '!'); 🙂


1 En realidad, es posible mutar cuerdas (y otros objetos inmutables). Requiere reflexión y es muy, muy peligroso y nunca debe usarse a menos que esté realmente interesado en destruir el progtwig.

El objeto que str referencia puede cambiar, pero los objetos String reales no pueden hacerlo.

Los objetos String que contienen la cadena "Hello" y "Help!" no puede cambiar sus valores, por lo tanto, son inmutables.

La inmutabilidad de los objetos String no significa que las referencias que apuntan al objeto no puedan cambiar.

Una forma en que uno puede evitar que la referencia str cambie es declararla como final :

 final String STR = "Hello"; 

Ahora, tratar de asignar otra String a STR provocará un error de comstackción.

Light_handle Te recomiendo que leas el tamaño de la copa: una historia sobre las variables y el valor Pass-by-Value Please (el tamaño de la copa continúa) . Esto ayudará mucho al leer las publicaciones anteriores.

¿Los has leído? Sí. Bueno.

 String str = new String(); 

Esto crea un nuevo “control remoto” llamado ” str ” y lo establece en el valor new String() (o "" ).

por ejemplo, en la memoria esto crea:

 str --- > "" 

 str = "Hello"; 

Esto luego cambia el control remoto ” str ” pero no modifica la cadena original "" .

por ejemplo, en la memoria esto crea:

 str -+ "" +-> "Hello" 

 str = "Help!"; 

Esto cambia el control remoto ” str ” pero no modifica la cadena original "" o el objeto al que actualmente apunta el control remoto.

por ejemplo, en la memoria esto crea:

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

Vamos a dividirlo en algunas partes

 String s1 = "hello"; 

Esta instrucción crea una cadena que contiene hello y ocupa espacio en la memoria, es decir, en el conjunto de cadenas constantes y lo asigna al objeto de referencia s1.

 String s2 = s1; 

Esta instrucción asigna la misma cadena de hola a la nueva referencia s2

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

Ambas referencias apuntan a la misma cadena, por lo que muestra el mismo valor de la siguiente manera.

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

Aunque String es inmutable , la asignación puede ser posible, por lo que s1 se referirá ahora a la nueva stack de valores.

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

Pero, ¿qué pasa con el objeto s2 que apunta hacia hello? Será como es.

  __________ | | s2 ---->| hello | |__________| out.println(s1); // o/p: stack out.println(s2); // o/p: hello 

Dado que String es inmutable, Java Virtual Machine no nos permitirá modificar la cadena s1 por su método. Creará todo el nuevo objeto String en el grupo de la siguiente manera.

 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 

Nota si String sería mutable, entonces la salida habría sido

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

Ahora se sorprenderá de por qué String tiene métodos tales como concat () para modificar. El siguiente fragmento borrará tu confusión.

 s1 = s1.concat(" overflow"); 

Aquí estamos asignando el valor modificado de la cadena a la referencia s1 .

  ___________________ | | s1 ---->| stack overflow | |___________________| out.println(s1); // o/p: stack overflow out.println(s2); // o/p: hello 

Es por eso que Java decidió que String sería una clase final. De lo contrario, cualquiera puede modificar y cambiar el valor de la cadena. Espero que esto ayude un poco.

El objeto cadena al que str se refirió primero no se alteró, todo lo que hizo fue hacer que str refiriera a un nuevo objeto de cadena.

La cadena no cambiará, la referencia a ella lo hará. Estás confundiendo la inmutabilidad con el concepto de campos final . Si un campo se declara como final , una vez que se ha asignado, no se puede reasignar.

En cuanto a la parte de reemplazo de su pregunta, intente esto:

 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! 

La inmutabilidad implica que el valor de un objeto instanciado no puede cambiar, nunca puede convertir “Hola” en “¡Ayuda!”.

La variable str es una referencia a un objeto, cuando asigna un nuevo valor a str no está cambiando el valor del objeto al que hace referencia, está haciendo referencia a un objeto diferente.

Aunque java intenta ignorarlo, str no es más que un puntero. Esto significa que cuando primero escribe str = "Hello"; , creas un objeto al que str apunta. Cuando reasignas str escribiendo str = "Help!"; , se crea un objeto nuevo y el antiguo objeto "Hello" obtiene la basura recolectada siempre que java lo desee.

Utilizar:

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

Si ve aquí, uso el método concat para cambiar la cadena original, es decir, “Nueva cadena” con una cadena “Cadena agregada”, pero igual obtuve el resultado como anterior, por lo tanto, prueba que no puede cambiar la referencia de objeto de la clase String, pero si haces esto por la clase StringBuilder, funcionará. Se enumera a continuación.

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

La clase de cadena es inmutable, y no puede cambiar el valor del objeto inmutable. Pero en el caso de String, si cambia el valor de la cadena, creará una cadena nueva en el grupo de cadenas y su referencia de cadena no será la anterior. así que, de esta manera, la cuerda es inmutable. Tomemos tu ejemplo,

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

creará una cadena “Mississippi” y la agregará al grupo de cadenas, por lo que ahora str apunta a Mississippi.

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

Pero después de la operación anterior, se creará otra secuencia “M! Ss! Ss! Pp!” y se agregará al grupo de cadenas. y ahora str apunta a M! ss! ss! pp !, no a Mississippi.

entonces, de esta forma, cuando altere el valor del objeto cadena, creará un objeto más y lo agregará al grupo de cadenas.

Permite tener un ejemplo más

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

esta línea arriba de tres agregará tres objetos de cadena al grupo de cadenas.
1) Hola
2) Mundo
3) HelloWorld

Como Linus Tolvards dijo:

Hablar es barato. Muéstrame el código

Mira esto:

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

El resultado es

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

Entonces, recuerda dos cosas:

  • Las cadenas son inmutables hasta que aplique un método que manipule y cree una nueva (casos de C & D).
  • El método Replace devuelve el mismo objeto String si ambos parámetros son iguales

Cadena en Java inmutable y final solo significa que no se puede cambiar o modificar:

Caso 1:

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

Salida: ABC

Motivo: La referencia de objeto str no se modifica; de hecho, se crea un nuevo objeto “DEF” que está en el grupo y no tiene ninguna referencia (es decir, perdido).

Caso 2

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

Salida: ABCDEF

Motivo: en este caso, str se refiere ahora a un nuevo objeto “ABCDEF”, por lo tanto imprime ABCDEF, es decir, el objeto str anterior “ABC” se pierde en el grupo sin referencia.

Para aquellos que se preguntan cómo romper la inmutabilidad de String en Java …

Código

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

Salida

 true I am mutable I am mutable 

La cadena es inmutable . Lo que significa que solo podemos cambiar la referencia .


 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 

En Java, generalmente se accede a los objetos por referencias. En su fragmento de código, str es una referencia que primero se asigna a “Hola” (un objeto creado automáticamente o que se obtiene del grupo de constantes ) y luego se le asigna otro objeto “¡Ayuda!” a la misma referencia. Un punto a tener en cuenta es que la referencia es la misma y está modificada, pero los objetos son diferentes. Una cosa más en tu código accediste a tres objetos,

  1. Cuando llamaste nuevo String ().
  2. Cuando asignó “hola”.
  3. Cuando asignó “ayuda”.

Llamar a new String () crea un nuevo objeto incluso si existe en el grupo de cadenas, por lo que, en general, no se debe usar. Para poner una cadena creada desde un nuevo String () en el grupo de cadenas, puede probar el método intern() .

Espero que esto ayude.

La inmutabilidad que puedo decir es que no puedes cambiar la cadena en sí. Supongamos que tiene String x, cuyo valor es “abc”. Ahora no puede cambiar la cadena, es decir, no puede cambiar ningún carácter / s en “abc”.

Si tiene que cambiar cualquier carácter / s en la Cadena, puede usar una matriz de caracteres y mutarla o usar 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); 

Salida:

 hj sj 

O puede intentar:

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

Esto mostrará cómo cambia el código hash.

La cadena es inmutable significa que no puede cambiar el objeto en sí, pero puede cambiar la referencia al objeto. Cuando llamaste a = “ty”, en realidad estás cambiando la referencia de a a un nuevo objeto creado por la “ty” literal String. Cambiar un objeto significa utilizar sus métodos para cambiar uno de sus campos (o los campos son públicos y no definitivos, de modo que se pueden actualizar desde el exterior sin acceder a ellos mediante métodos), por ejemplo:

 Foo x = new Foo("the field"); x.setField("a new field"); System.out.println(x.getField()); // prints "a new field" 

Mientras esté en una clase inmutable (declarada como final, para evitar modificaciones mediante herencia) (sus métodos no pueden modificar sus campos, y los campos son siempre privados y recomendados para ser finales), por ejemplo, String, no puede cambiar la cadena actual, pero usted puede devolver un nuevo String, es decir:

 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" 

Aquí la inmutabilidad significa que la instancia puede apuntar a otra referencia, pero el contenido original de la cadena no se modificará en la referencia original. Déjame explicarte con el primer ejemplo dado por ti. First str apunta a “Hola”, está bien hasta esto. La segunda vez apunta hacia “¡Ayuda!”. Aquí str comenzó a apuntar a “¡Ayuda!” y la referencia de la cadena “Hola” se pierde y no podemos recuperarla.

De hecho, cuando str intente modificar el contenido existente, se generará otra cadena nueva y str comenzará a apuntar a esa referencia. Entonces vemos que la cadena en la referencia original no se modifica pero que es segura en su referencia y la instancia del objeto comenzó a apuntar a una referencia diferente, por lo que se conserva la inmutabilidad.

Súper tarde a la respuesta, pero quería poner un mensaje conciso del autor de la clase String en Java

Las cadenas son constantes; sus valores no se pueden cambiar después de que se crean. Los búferes de cadena admiten cadenas mutables. Debido a que los objetos String son inmutables, se pueden compartir.

De esta documentación se deriva que todo lo que cambia cadena, devuelve un objeto diferente (que podría ser nuevo o interno y antiguo). La sugerencia no tan sutil sobre esto debería provenir de la firma de la función. Piénselo, ‘¿Por qué hacen que una función en un objeto devuelva un objeto en lugar de un estado?’.

 public String replace(char oldChar, char newChar) 

También una fuente más que hace que este comportamiento sea explícito (Reemplazar la documentación de la función)

Devuelve una nueva cadena resultante de reemplazar todas las ocurrencias de oldChar en esta cadena con newChar.

Fuente: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replace(char,%20char)

  • autor Lee Boynton
  • autor Arthur van Hoff
  • autor Martin Buchholz
  • autor Ulf Zibis

Fuente: JavaDoc de String.

La cadena de objetos: los métodos en sí están hechos para ser “inmutables”. Esta acción no produce cambios: “letters.replace (” bbb “,” aaa “);”

Pero la asignación de datos provoca cambios en el contenido de Strings para cambiar:

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

// El código hash de la cadena El objeto no cambia.

Debido a que String es inmutable, los cambios no se producirán si no asignará el valor devuelto de la función a la cadena. En su pregunta, asigne el valor de la función swap al valor devuelto a s.

s = swap (s, n1, n2); luego cambiará el valor de la cadena s.

También estaba obteniendo el valor sin cambios cuando estaba escribiendo el progtwig para obtener una serie de permutaciones (aunque no da todas las permutaciones, pero esto es por ejemplo para responder a su pregunta)

Aquí hay un ejemplo.

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

pero no obtenía los valores permutados de la cadena del código anterior. Así que asigné el valor devuelto de la función de intercambio a la cadena y obtuve los valores cambiados de la cadena. después de asignar el valor devuelto, obtuve los valores permutados de la cadena.

 /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 

Si HELLO es tu Cadena, entonces no puedes cambiar HELLO a HILLO . Esta propiedad se llama propiedad de inmutabilidad.

Puede tener una variable String de múltiples punteros para apuntar HOLA cadena.

Pero si HELLO es Char Array, entonces puedes cambiar HELLO a HILLO. P.ej,

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

Los lenguajes de progtwigción tienen variables de datos inmutables para que se puedan usar como claves en clave, par de valores.