java - usar - La cadena es inmutable. ¿Cuál es exactamente el significado?
stringbuilder c# (19)
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 declaración 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 declaración 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:
- La primera línea es bastante simple: cree una nueva
String
"java"
y remitas1
a ella. - A continuación, la máquina virtual crea otra nueva
String
"java rules"
, pero nada se refiere a ella. Entonces, la segundaString
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 objetivos clave de cualquier lenguaje de programació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 compilador 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 objetivo 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.
Esta pregunta ya tiene una respuesta aquí:
- Inmutabilidad de cadenas en Java 23 respuestas
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-->a
a 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
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);
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
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.
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.
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
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";
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
La cadena es inmutable significa que el contenido del objeto String no puede ser modificable
si quieres modificar el contenido utiliza StringBuffer en lugar de String que es mutable
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"
No está cambiando el objeto en la declaración 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.
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
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.
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.
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.
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 programadores 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
interned 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.
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.
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