efecto de cambiar String usando la reflexión
Como todos sabemos, String es inmutable en Java. sin embargo, se puede cambiar mediante la reflexión, obteniendo el Campo y configurando el nivel de acceso. (Sé que no es aconsejable, no planeo hacerlo, esta pregunta es puramente teórica).
Mi pregunta: suponiendo que sepa lo que estoy haciendo (y modifique todos los campos según sea necesario), ¿se ejecutará correctamente el programa? ¿O la jvm realiza algunas optimizaciones que dependen de que String sea inmutable? ¿Sufriré pérdida de rendimiento? Si es así, ¿qué suposición hace? ¿Qué saldrá mal en el programa?
ps String es solo un ejemplo, en realidad estoy interesado en una respuesta general, además del ejemplo.
¡gracias!
Después de la compilación, algunas cadenas pueden hacer referencia a una instancia, por lo que editará más de lo que desee y nunca sabrá qué más está editando.
public static void main(String args[]) throws Exception {
String s1 = "Hello"; // I want to edit it
String s2 = "Hello"; // It may be anywhere and must not be edited
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set(s1, "Doesn't say hello".toCharArray());
System.out.println(s2);
}
Producción:
Doesn't say hello
Definitivamente te estás buscando problemas si haces esto. ¿Eso significa que definitivamente verás errores de inmediato? No. Es posible que te salgas con la tuya en muchos casos, dependiendo de lo que estés haciendo.
Aquí hay un par de casos en los que te mordería:
- Modificas una cadena que ha sido declarada como literal en algún lugar del código. Por ejemplo, tienes un
function
y en algún lugar se llama comofunction("Bob")
; en este escenario, la cadena"Bob"
cambia en toda su aplicación (esto también será válido para las constantes de cadena declaradas comofinal
). - Modifica una cadena que se utiliza en operaciones de subcadena o que es el resultado de una operación de subcadena. En Java, tomar una subcadena de una cadena en realidad utiliza la misma matriz de caracteres subyacente que la cadena fuente, lo que significa que las modificaciones a la cadena fuente afectarán a las subcadenas (y viceversa).
- Modificas una cadena que se utiliza como clave en un mapa en algún lugar. Ya no se comparará con su valor original, por lo que las búsquedas fallarán.
Sé que esta pregunta es sobre Java, pero hace un tiempo escribí una publicación en el blog que ilustra cuán loco puede comportarse su programa si muta una cadena en .NET. Las situaciones son realmente bastante similares.
Lo que me viene a la mente es la internación de cadenas: literales, cualquier cosa en el grupo constante y cualquier cosa intern()
editada manualmente apunta al mismo objeto de cadena. Si comienza a jugar con el contenido de una cadena literal interna, es posible que vea exactamente las mismas alteraciones en todos los demás literales que utilizan el mismo objeto subyacente.
No estoy seguro de si lo anterior realmente sucede ya que nunca lo he intentado (en teoría sucederá, no sé si sucede algo debajo de la escena para detenerlo, pero lo dudo) pero son cosas así las que podrían vomitar. problemas potenciales. Por supuesto, también podría generar problemas a nivel de Java simplemente pasando múltiples referencias a la misma cadena y luego usando un ataque de reflexión para alterar el objeto de una de las referencias. La mayoría de las personas (¡incluido yo!) no se protegerán explícitamente contra ese tipo de cosas en el código, por lo que usar ese ataque con cualquier código que no sea el suyo, o su propio código si tampoco se ha protegido contra eso, podría causar todo tipo de bichos extraños y horribles.
Es un área interesante en teoría, pero cuanto más investigas, más ves por qué algo como esto es una mala idea.
Hablando fuera de la cadena, no conozco mejoras de rendimiento para un objeto que sea inmutable (de hecho, no creo que la JVM pueda saber en este momento si un objeto es inmutable, dejando de lado los ataques de reflexión). Podría arrojar cosas como el verificador -framework off o cualquier cosa que intente analizar estáticamente el código para garantizar que sea inmutable.
Estoy bastante seguro de que la JVM en sí no hace suposiciones sobre la inmutabilidad de las cadenas, ya que la "inmutabilidad" en Java no es una construcción a nivel de lenguaje; es un rasgo implícito en la implementación de una clase, pero, como usted observa, no puede garantizarse en presencia de reflexión. Por lo tanto, tampoco debería ser relevante para el rendimiento.
Sin embargo, casi todo el código Java existente (incluida la implementación de la API estándar) depende de que las cadenas sean inmutables, y si incumple esa expectativa, verá todo tipo de errores.