Tengo una clase con un private static finalcampo que, desafortunadamente, necesito cambiarlo en tiempo de ejecución.
Usando la reflexión me sale este error: java.lang.IllegalAccessException: Can not set static final boolean field
¿Hay alguna forma de cambiar el valor?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);

System.out/in/errson tan "especiales" que el modelo de memoria de Java debe hacer una mención especial de ellos. No son ejemplos que deben seguirse.Respuestas:
Suponiendo que no
SecurityManagerte impide hacer esto, puedes usarlosetAccessiblepara desplazarteprivatey restablecer el modificador para deshacerte de élfinaly, de hecho, modificar unprivate static finalcampo.Aquí hay un ejemplo:
Asumiendo que no
SecurityExceptionse lanza, se imprime el código anterior"Everything is true".Lo que realmente se hace aquí es lo siguiente:
booleanvalores primitivostrueyfalseenmainse autoboxing al tipo de referenciaBoolean"constantes"Boolean.TRUEyBoolean.FALSEpublic static final Boolean.FALSEreferencia a laBooleanreferencia deBoolean.TRUEfalsese autoboxed aBoolean.FALSE, se refiere a la mismaBooleancomo el referido a porBoolean.TRUE"false"ahora es"true"Preguntas relacionadas
static final File.separatorCharpara pruebas unitariasIntegerel caché, mutar aString, etc.Advertencias
Se debe tener mucho cuidado cuando haga algo como esto. Es posible que no funcione porque
SecurityManagerpuede estar presente, pero incluso si no lo hace, dependiendo del patrón de uso, puede funcionar o no.Ver también
private static final boolean, porque es inlineable como una constante de tiempo de compilación y, por lo tanto, el valor "nuevo" puede no ser observableApéndice: sobre la manipulación bit a bit
Esencialmente,
apaga el bit correspondiente a
Modifier.FINALfromfield.getModifiers().&es el bit a bit y~el complemento a bit.Ver también
Recuerda expresiones constantes
¿Aún no puedes resolver esto ?, ¿has caído en la depresión como lo hice yo? ¿Su código se ve así?
Al leer los comentarios sobre esta respuesta, especialmente la de @Pshemo, me recordó que las expresiones constantes se manejan de manera diferente, por lo que será imposible modificarla. Por lo tanto, deberá cambiar su código para que se vea así:
si no eres el dueño de la clase ... te siento!
Para más detalles sobre por qué este comportamiento lee esto ?
fuente
getDeclaredField()en lugar degetField()para la clase de objetivofinal String myConstant = "x";y fallarán: recuerden que las constantes de tiempo de compilación serán alineadas por el compilador, de modo que cuando escriban código comoSystem.out.println(myConstant);si fuera compilado comoSystem.out.println("x");porque el compilador conoce el valor de la constante en el momento de la compilación. Para deshacerse de este problema, debe inicializar sus constantes en tiempo de ejecución comofinal String myConstant = new String("x");. También en caso de primitivas como elfinal int myField = 11usofinal int myField = new Integer(11);ofinal Integer myField = 11;Si el valor asignado a un
static final booleancampo se conoce en tiempo de compilación, es una constante. Los campos de primitivo oStringtipo pueden ser constantes de tiempo de compilación. Se incluirá una constante en cualquier código que haga referencia al campo. Como el campo no se lee realmente en tiempo de ejecución, cambiarlo no tendrá ningún efecto.La especificación del lenguaje Java dice esto:
Aquí hay un ejemplo:
Si descompila
Checker, verá que en lugar de hacer referenciaFlag.FLAG, el código simplemente empuja un valor de 1 (true) en la pila (instrucción # 3).fuente
public static final Boolean FALSE = new Boolean(false)nopublic static final boolean FALSE = falseUn poco de curiosidad de la Especificación del lenguaje Java, capítulo 17, sección 17.5.4 "Campos protegidos contra escritura":
Fuente: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
fuente
También lo integré con joor library
Solo usa
También solucioné un problema con el
overrideque las soluciones anteriores parecen fallar. Sin embargo, use esto con mucho cuidado, solo cuando no haya otra buena solución.fuente
Junto con la respuesta mejor clasificada, puede utilizar un enfoque un poco más simple. La
FieldUtilsclase commons de Apache ya tiene un método particular que puede hacer las cosas. Por favor, eche un vistazo alFieldUtils.removeFinalModifiermétodo. Debe especificar la instancia de campo de destino y la marca de forzamiento de accesibilidad (si juega con campos no públicos). Más información puedes encontrar aquí .fuente
java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.En caso de presencia de un administrador de seguridad, uno puede hacer uso de
AccessController.doPrivilegedTomando el mismo ejemplo de la respuesta aceptada arriba:
En la expresión lambda
AccessController.doPrivileged, se puede simplificar a:fuente
La respuesta aceptada funcionó para mí hasta que se implementó en JDK 1.8u91. Luego me di cuenta de que fallaba en la
field.set(null, newValue);línea cuando había leído el valor mediante reflexión antes de llamar alsetFinalStaticmétodo.Probablemente la lectura causó de alguna manera una configuración diferente de los elementos internos de reflexión de Java (es decir,
sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImplen caso de error en lugar desun.reflect.UnsafeStaticObjectFieldAccessorImplen caso de éxito), pero no lo desarrollé más.Como necesitaba establecer temporalmente un nuevo valor basado en el valor anterior y luego volver a establecer el valor anterior, cambié un poco la firma para proporcionar externamente la función de cálculo y también devolver el valor anterior:
Sin embargo, para un caso general, esto no sería suficiente.
fuente
Incluso a pesar de ser
finalun campo puede modificarse fuera del inicializador estático y (al menos JVM HotSpot) ejecutará el código de bytes perfectamente bien.El problema es que el compilador de Java no permite esto, pero esto puede pasarse por alto fácilmente
objectweb.asm. Aquí hay un archivo de clase perfectamente válido que pasa la verificación de bytecode y se carga e inicializa con éxito en JVM HotSpot OpenJDK12:En Java, la clase se ve más o menos como sigue:
que no se puede compilar con
javac, pero JVM puede cargar y ejecutar.JVM HotSpot tiene un tratamiento especial de tales clases en el sentido de que evita que tales "constantes" participen en el plegado constante. Esta comprobación se realiza en la fase de reescritura de bytecode de inicialización de clase :
La única restricción que comprueba JVM HotSpot es que el
finalcampo no debe modificarse fuera de la clase en la quefinalse declara el campo.fuente
Acabo de ver esa pregunta en una de las preguntas de la entrevista, si es posible cambiar la variable final con reflexión o en tiempo de ejecución. Me interesé mucho, así que con lo que me convertí:
Alguna clase simple con la variable de cadena final. Entonces, en la clase principal import java.lang.reflect.Field;
El resultado será el siguiente:
De acuerdo con la documentación https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
fuente
staticcampo final, por lo que este código no funciona.setAccessible(true)solo funciona para establecer campos de instancia final.Si su campo es simplemente privado, puede hacer esto:
y lanzar / manejar NoSuchFieldException
fuente
Todo el punto de un
finalcampo es que no se puede reasignar una vez establecido. La JVM utiliza esta garantía para mantener la coherencia en varios lugares (por ejemplo, clases internas que hacen referencia a variables externas). Entonces no. ¡Ser capaz de hacerlo rompería la JVM!La solución es no declararlo
finalen primer lugar.fuente
finaltiene un papel especial en la ejecución multiproceso: cambiar losfinalvalores también rompería el modelo de memoria Java.finalno debe ser declaradostatic.