J'ai une classe avec un private static final
champ que, malheureusement, je dois le changer au moment de l'exécution.
En utilisant la réflexion, j'obtiens cette erreur: java.lang.IllegalAccessException: Can not set static final boolean field
Existe-t-il un moyen de modifier la valeur?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
System.out/in/err
sont si "spéciaux" que le modèle de mémoire Java doit en faire une mention spéciale. Ce ne sont pas des exemples à suivre.Réponses:
En supposant que non
SecurityManager
vous empêche de le faire, vous pouvez utilisersetAccessible
pour vous déplacerprivate
et réinitialiser le modificateur pour vous en débarrasserfinal
, et modifier réellement unprivate static final
champ.Voici un exemple:
En supposant que non
SecurityException
est levé, le code ci-dessus s'imprime"Everything is true"
.Ce qui est réellement fait ici est le suivant:
boolean
valeurs primitivestrue
etfalse
inmain
sont automatiquement encadrées pour faire référence auxBoolean
constantes de typeBoolean.TRUE
etBoolean.FALSE
public static final Boolean.FALSE
faire référence à laBoolean
cité parBoolean.TRUE
false
est autoboxed àBoolean.FALSE
, elle fait référence à la mêmeBoolean
que celle référée parBoolean.TRUE
"false"
maintenant est"true"
Questions connexes
static final File.separatorChar
pour les tests unitairesInteger
le cache de, de muter unString
, etc.Avertissements
Un soin extrême doit être pris chaque fois que vous faites quelque chose comme ça. Cela peut ne pas fonctionner car un
SecurityManager
peut être présent, mais même si ce n'est pas le cas, selon le modèle d'utilisation, cela peut ou peut ne pas fonctionner.Voir également
private static final boolean
, car elle est inlineable comme constante de temps de compilation et donc la "nouvelle" valeur peut ne pas être observableAnnexe: sur la manipulation au niveau du bit
Essentiellement,
désactive le bit correspondant à
Modifier.FINAL
fromfield.getModifiers()
.&
est le bit à bit et et~
est le complément au bit.Voir également
N'oubliez pas les expressions constantes
Vous ne parvenez toujours pas à résoudre ce problème?, Êtes tombé dans la dépression comme je l'ai fait pour cela? Votre code ressemble à ceci?
En lisant les commentaires sur cette réponse, en particulier celle de @Pshemo, cela m'a rappelé que les expressions constantes sont traitées différemment, il sera donc impossible de la modifier. Par conséquent, vous devrez modifier votre code pour qu'il ressemble à ceci:
si vous n'êtes pas le propriétaire de la classe ... je vous sens!
Pour plus de détails sur la raison de ce comportement, lisez ceci ?
la source
getDeclaredField()
au lieu degetField()
pour la classe ciblefinal String myConstant = "x";
et échoueront: rappelez-vous que les constantes de temps de compilation seront alignées par le compilateur, donc quand vous écrirez du code commeSystem.out.println(myConstant);
il sera compilé commeSystem.out.println("x");
parce que le compilateur connaît la valeur de constante au moment de la compilation. Pour vous débarrasser de ce problème, vous devez initialiser vos constantes lors de l'exécution commefinal String myConstant = new String("x");
. Aussi en cas de primitives commefinal int myField = 11
utilisationfinal int myField = new Integer(11);
oufinal Integer myField = 11;
Si la valeur affectée à un
static final boolean
champ est connue au moment de la compilation, il s'agit d'une constante. Les champs deString
type primitif ou de type peuvent être des constantes au moment de la compilation. Une constante sera insérée dans tout code faisant référence au champ. Étant donné que le champ n'est pas réellement lu au moment de l'exécution, sa modification n'aura alors aucun effet.La spécification du langage Java dit ceci:
Voici un exemple:
Si vous décompilez
Checker
, vous verrez qu'au lieu de faire référenceFlag.FLAG
, le code pousse simplement une valeur de 1 (true
) sur la pile (instruction # 3).la source
public static final Boolean FALSE = new Boolean(false)
paspublic static final boolean FALSE = false
Un peu de curiosité de la spécification du langage Java, chapitre 17, section 17.5.4 "Champs protégés en écriture":
Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
la source
Je l'ai également intégré à la bibliothèque Joor
Utilisez simplement
J'ai également corrigé un problème avec
override
lequel les solutions précédentes semblent manquer. Cependant, utilisez-le très soigneusement, uniquement lorsqu'il n'y a pas d'autre bonne solution.la source
Avec la réponse la mieux classée, vous pouvez utiliser une approche un peu plus simple. La
FieldUtils
classe Apache commons a déjà une méthode particulière qui peut faire les choses. Veuillez regarder laFieldUtils.removeFinalModifier
méthode. Vous devez spécifier l'instance de champ cible et l'indicateur de forçage d'accessibilité (si vous jouez avec des champs non publics). Plus d'informations que vous pouvez trouver ici .la source
java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.
En cas de présence d'un Security Manager, on peut utiliser
AccessController.doPrivileged
En prenant le même exemple de la réponse acceptée ci-dessus:
Dans l'expression lambda,,
AccessController.doPrivileged
peut être simplifié pour:la source
La réponse acceptée a fonctionné pour moi jusqu'à son déploiement sur JDK 1.8u91. Ensuite, j'ai réalisé qu'il avait échoué à la
field.set(null, newValue);
ligne lorsque j'avais lu la valeur par réflexion avant d'appeler lasetFinalStatic
méthode.La lecture a probablement causé une configuration différente des internes de réflexion Java (à savoir
sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
en cas d'échec plutôtsun.reflect.UnsafeStaticObjectFieldAccessorImpl
qu'en cas de réussite), mais je ne l'ai pas développé davantage.Comme j'avais besoin de définir temporairement une nouvelle valeur en fonction de l'ancienne valeur et de la rétablir ultérieurement, j'ai légèrement modifié la signature pour fournir une fonction de calcul en externe et également retourner l'ancienne valeur:
Cependant, dans le cas général, cela ne serait pas suffisant.
la source
Même en dépit d'être
final
un champ peut être modifié en dehors de l'initialiseur statique et (au moins JVM HotSpot) exécutera parfaitement le bytecode.Le problème est que le compilateur Java ne permet pas cela, mais cela peut être facilement contourné en utilisant
objectweb.asm
. Voici un fichier de classe parfaitement valide qui passe la vérification du bytecode et qui est correctement chargé et initialisé sous JVM HotSpot OpenJDK12:En Java, la classe ressemble grosso modo à ce qui suit:
qui ne peut pas être compilé avec
javac
, mais peut être chargé et exécuté par JVM.JVM HotSpot a un traitement spécial pour ces classes dans le sens où il empêche ces "constantes" de participer au repliement constant. Cette vérification est effectuée lors de la phase de réécriture du bytecode de l'initialisation de la classe :
La seule restriction que JVM HotSpot vérifie est que le
final
champ ne doit pas être modifié en dehors de la classe à laquelle lefinal
champ est déclaré.la source
Je viens de voir cette question sur l'une des questions de l'interview, si possible pour changer la variable finale avec réflexion ou en runtime. Je suis vraiment intéressé, donc ce que je suis devenu avec:
Une classe simple avec une variable String finale. Ainsi, dans la classe principale, importez java.lang.reflect.Field;
La sortie sera la suivante:
Selon la documentation https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
la source
static
champ final, donc ce code ne fonctionne pas.setAccessible(true)
ne fonctionne que pour définir les champs d'instance finale.Si votre domaine est simplement privé, vous pouvez le faire:
et lancer / gérer NoSuchFieldException
la source
L'intérêt d'un
final
champ est qu'il ne peut pas être réaffecté une fois défini. La JVM utilise cette garantie pour maintenir la cohérence à divers endroits (par exemple, les classes internes référençant les variables externes). Donc non. Le faire pourrait briser la JVM!La solution n'est pas de le déclarer
final
en premier lieu.la source
final
a un rôle spécial dans l'exécution multithread - la modification desfinal
valeurs briserait également le modèle de mémoire Java.final
ne doit pas être déclaréstatic
.