La cadena es inmutable. ¿Cuál es exactamente el significado?

Escribí el siguiente código en cadenas inmutables.

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

Salida:

 a 1-->aa 2-->ty 

Aquí el valor de la variable a ha sido cambiado (mientras que muchos dicen que el contenido de los objetos inmutables no se puede cambiar). Pero, ¿qué significa exactamente decir que String es inmutable ? ¿Podría aclarar este tema para mí?

fuente: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Antes de seguir adelante con el alboroto de la inmutabilidad , echemos un vistazo a la clase String y su funcionalidad un poco antes de llegar a una conclusión.

Así es como funciona String :

 String str = "knowledge"; 

Esto, como de costumbre, crea una cadena que contiene "knowledge" y le asigna una referencia str . ¿Suficientemente simple? Vamos a realizar algunas funciones más:

  String s = str; // assigns a new reference to the same string "knowledge" 

Veamos cómo funciona la siguiente afirmación:

  str = str.concat(" base"); 

Esto agrega una cadena " base" a str . Pero espera, ¿cómo es posible esto, ya que los objetos String son inmutables? Bueno, para su sorpresa, lo es.

Cuando se ejecuta la statement anterior, la VM toma el valor de String str , es decir, "knowledge" y agrega " base" , dándonos el valor "knowledge base" . Ahora, dado que las String son inmutables, la VM no puede asignar este valor a str , por lo que crea un nuevo objeto String , le da un valor de "knowledge base" y le da una referencia str .

Un punto importante a tener en cuenta aquí es que, aunque el objeto String es inmutable, su variable de referencia no lo es. Por eso, en el ejemplo anterior, la referencia se hizo para referirse a un objeto String recién formado.

En este punto del ejemplo anterior, tenemos dos objetos String : el primero que creamos con valor "knowledge" , apuntado por s , y el segundo "knowledge base" , apuntado por str . Pero, técnicamente, tenemos tres objetos String , el tercero es la "base" literal en la statement de concat .

Hechos importantes sobre el uso de cadenas y memoria

¿Qué pasaría si no tuviéramos otra referencia al "knowledge" ? Hubiéramos perdido esa String . Sin embargo, todavía habría existido, pero se consideraría perdido debido a no tener referencias. Mira un ejemplo más abajo

 String s1 = "java"; s1.concat(" rules"); System.out.println("s1 refers to "+s1); // Yes, s1 still refers to "java" 

Qué esta pasando:

  1. La primera línea es bastante simple: cree una nueva String "java" y remita s1 a ella.
  2. A continuación, la máquina virtual crea otra nueva String "java rules" , pero nada se refiere a ella. Entonces, la segunda String se pierde al instante. No podemos alcanzarlo.

La variable de referencia s1 todavía se refiere a la String original "java" .

Casi todos los métodos, aplicados a un objeto String para modificarlo, crean un nuevo objeto String . Entonces, ¿a dónde van estos objetos String ? Bueno, estos existen en la memoria, y uno de los objectives clave de cualquier lenguaje de progtwigción es hacer un uso eficiente de la memoria.

A medida que crecen las aplicaciones, es muy común que los literales de String ocupen un área grande de memoria, lo que incluso puede generar redundancia. Por lo tanto, para hacer que Java sea más eficiente, la JVM reserva un área especial de memoria denominada “conjunto constante de cadenas”.

Cuando el comstackdor ve un literal String , busca el String en el grupo. Si se encuentra una coincidencia, la referencia al nuevo literal se dirige a la String existente y no se crea un nuevo objeto String . La String existente simplemente tiene una referencia más. Aquí viene el objective de hacer que los objetos String sean inmutables:

En el conjunto de String constante String , es probable que un objeto String tenga una o varias referencias. Si varias referencias apuntan a la misma String sin siquiera saberlo, sería malo si una de las referencias modificara ese valor de String . Es por eso que los objetos String son inmutables.

Bueno, ahora podrías decir, ¿qué pasa si alguien anula la funcionalidad de String clase String ? Esa es la razón por la cual la clase String se marca como final para que nadie pueda anular el comportamiento de sus métodos.

La cadena es inmutable significa que no puede cambiar el objeto en sí, pero puede cambiar la referencia al objeto.

Cuando ejecuta a = "ty" , en realidad está cambiando la referencia de a a un nuevo objeto creado por el literal "ty" 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" 

Estás cambiando a lo que a refiere . Prueba esto:

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

Verás que el objeto al que se refieren a y luego b no ha cambiado.

Si quiere evitar que su código cambie a qué objeto hace referencia, intente:

 final String a="a"; 

Una cadena es un char[] contiene una serie de unidades de código UTF-16 , un desplazamiento int en esa matriz y una longitud int .

Por ejemplo.

 String s 

Crea espacio para una referencia de cadena. La asignación de copias copia referencias pero no modifica los objetos a los que se refieren esas referencias.

También debes saber que

 new String(s) 

realmente no hace nada útil. Simplemente crea otra instancia respaldada por la misma matriz, desplazamiento y longitud que s . Raramente hay una razón para hacer esto, por lo que la mayoría de los progtwigdores de Java lo consideran una mala práctica.

Las cadenas de Java entre comillas dobles como "my string" son realmente referencias a instancias de String internas String por lo que "bar" es una referencia a la misma instancia de Cadena, independientemente de cuántas veces aparezca en tu código.


El “saludo” crea una instancia que se agrupa, y la new String(...) crea una instancia no agrupada. Pruebe System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello"))); y deberías ver true,false,false

inmutable significa que no puede cambiar el valor de la misma referencia. Cada vez que necesite crear una nueva referencia significa una nueva ubicación en la memoria. ex:

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

aquí, en el código anterior, en la memoria hay 2 bloques para almacenar el valor. El primero para el valor “abc” y el segundo para “bcd”. El segundo valor no se reemplaza al primer valor.

esto es llamar lo inmutable.

En su ejemplo, la variable a es solo una referencia a una instancia de un objeto de cadena. Cuando dice a = "ty" , en realidad no está cambiando el objeto de cadena, sino que apunta la referencia a una instancia completamente diferente de la clase de cadena.

mira aquí

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

salida:

 a 1-->a a 1 address-->97 a 2-->ty a 2 address-->3717 

Esto indica que cada vez que modifique el contenido del objeto de cadena inmutable, se creará un nuevo objeto. es decir, no está permitido cambiar el contenido del objeto inmutable. es por eso que la dirección es diferente para ambos el objeto.

No está cambiando el objeto en la statement de asignación, reemplaza un objeto inmutable por otro. Object String("a") no cambia a String("ty") , se descarta, y una referencia a ty se escribe en a en su lugar.

En contraste, StringBuffer representa un objeto mutable . Puedes hacerlo:

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

Aquí, no reasignó b : todavía apunta al mismo objeto, pero el contenido de ese objeto ha cambiado.

En realidad, está obteniendo una referencia a una nueva cadena, la cadena en sí misma no se cambia ya que es inmutable. Esto es relevante.

Ver

Objetos inmutables en Wikipedia

Un objeto inmutable es un objeto cuyo estado no se puede modificar después de haber sido creado.

Entonces a = "ABC" < - objeto inmutable. "a" contiene referencia al objeto. Y, a = “DEF” < - otro objeto inmutable, "a" guarda referencia ahora.

Una vez que asigne un objeto de cadena, ese objeto no se puede cambiar en la memoria.

En resumen, lo que hizo fue cambiar la referencia de “a” a un nuevo objeto de cadena.

 String S1="abc"; S1.concat("xyz"); System.out.println("S1 is", + S1); String S2=S1.concat("def"); System.out.println("S2 is", + S2); 

Esto muestra que una vez que se crea un objeto de cadena, no se puede cambiar. Cada vez que necesite crear algo nuevo y ponerlo en otra Cadena. S

Creo que el siguiente código aclara la diferencia:

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

Java String es inmutable, String almacenará el valor en forma de objeto. así que si asigna el valor String a="a"; creará un objeto y el valor se almacenará en eso y de nuevo si está asignando valor a="ty" significa que creará otro objeto almacenar el valor en eso, si quiere entender claramente, verifique el has code para el String

Solo la referencia está cambiando. Primero a estaba haciendo referencia a la cadena “a”, y más tarde lo cambió a “ty”. La cadena “a” sigue siendo la misma.

En su ejemplo, a refiere primero a "a" , y luego a "ty" . No está mutando ninguna instancia de String ; solo estás cambiando a qué instancia de String a hace referencia. Por ejemplo, esto:

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

imprime “a”, porque nunca muteamos la instancia de String que b apunta.

Si alguna bar objetos contiene una referencia a un objeto mutable foo y encapsula parte de su estado en aspectos mutables del estado de foo , eso permitirá que el código que puede cambiar esos aspectos de foo cambie los aspectos correspondientes del estado de la bar sin realmente tocando la bar o incluso sabiendo de su existencia . En general, esto significa que los objetos que encapsulan su propio estado utilizando objetos mutables deben garantizar que ninguna referencia a esos objetos esté expuesta a ningún código que pueda mutarlos inesperadamente. Por el contrario, si bar contiene una referencia a un objeto moo y solo utiliza aspectos inmutables de moo distintos de identidad para encapsular su estado, la bar puede exponer libremente moo a código externo sin preocuparse por nada que el código externo pueda hacerle.

Espero que el siguiente código aclare tus dudas:

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

Antes de Cadena Concat: Hola
Después de Cadena Concat: Hola
Antes de agregar StringBuffer: Hola
Después de agregar StringBuffer: HelloWorld

Probablemente cada respuesta proporcionada arriba es correcta, pero mi respuesta es específica al uso del método hashCode() , para probar puntos como, String … una vez creado no se puede modificar y las modificaciones darán como resultado un nuevo valor en una ubicación de memoria diferente.

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

SALIDA

 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 

La cadena es inmutable significa que el contenido del Objeto String no se puede cambiar una vez que se ha creado. Si desea modificar el contenido, puede utilizar StringBuffer / StringBuilder en lugar de String. StringBuffer y StringBuilder son clases mutables.