2012-06-22 11 views
11

Como casi todo el mundo sabe, las cadenas en Java son inmutables. Recientemente descubrí algo que podría sugerir que no siempre es verdad. Vamos a probar este código:Cadenas mutables en Java

System.out.println("-------- BEFORE MODIFICATIONS --------"); 
String beforeTest = new String("Original"); 
System.out.println(beforeTest); 
java.lang.reflect.Field valueField = String.class.getDeclaredField("value"); 
valueField.setAccessible(true); 
valueField.set("Original", "Modified".toCharArray()); 
System.out.println("-------- AFTER MODIFICATIONS --------"); 
System.out.println(beforeTest); 
System.out.println("Original"); 
String test = new String("Original"); 
System.out.println(test); 
String test2 = new String("Original 2"); 
System.out.println(test2); 

la salida sería:

-------- BEFORE MODIFICATIONS -------- 
Original 
-------- AFTER MODIFICATIONS -------- 
Original 
Modified 
Modified 
Original 2 

¿Cómo funciona este truco? ¿Cómo sabe la JVM qué objetos se deben cambiar y cuáles no? ¿Qué mecanismo están bajo el capó de este truco? ¿Por qué la cadena beforeTest ya creada no se modificó? ¿Este truco realmente deroga el principio strings are immutable?

+6

La reflexión es magia negra vudú. –

+1

@HovercraftFullOfEels, Reflection está perfectamente definido. Solo cuando violas 'privado' al llamar a 'setAccessible', las invariantes de la clase central se apagan. –

+3

@MikeSamuel Reflection * en sí * está bien definido. * Usar * no lo es, por lo tanto, el vudú una vez que se extrae lo inamovible comienza. Tengo un marco completo para esto (Muckito). –

Respuesta

17

Los literales de cadena están internados en un grupo. Esto significa que cuando se escribe

String s1 = "Foo"; 
String s2 = "Foo"; 
String s3 = new String("Foo"); 

s1 y s2 se refieren al mismo objeto String, y s3 ​​se refiere a otro, respaldado por otra matriz de caracteres.

En su código, viola las invariantes de String modificando la matriz de caracteres privada que contiene los caracteres de la instancia literal de Cadena "Original". Pero dado que beforeTest se refiere a otra instancia de cadena, no se modifica.

La inmutabilidad se logra manteniendo los campos privados en un objeto y sin proporcionar ningún método para modificar este estado privado. Al usar la reflexión, rompes todas las reglas de encapsulación, y así puedes violar la inmutabilidad.

+0

¿La tercera instancia (referencia de prueba) tampoco está apuntando a una nueva instancia? Si el carácter literal "Original" se reemplaza por "Modificado", ¿no debería actualizarse antes de probar el valor (porque también se pasa el mismo literal, por ejemplo)? – kosa

+0

La variable 'test' se inicializa en una copia del literal" Original "String. Pero ya ha modificado su contenido a "Modificado" cuando se realiza la copia. Entonces es una copia de "Modificado". 'beforeTest' es también una copia del literal" Original "String, pero la copia se ha realizado antes de modificar su contenido. –