578 votes

Changer le champ final statique privé en utilisant la réflexion Java

J'ai une classe avec un champ private static final que, malheureusement, j'ai besoin de 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

Est-il possible de changer la valeur?

 Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
 

1026voto

polygenelubricants Points 136838

En supposant qu'aucun SecurityManager vous empêche de faire cela, vous pouvez utiliser setAccessible de contourner private et la réinitialisation de l'indicateur pour se débarrasser de l' final, et en fait de modifier un private static final champ.

Voici un exemple:

import java.lang.reflect.*;

public class EverythingIsTrue {
   static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }
   public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);

      System.out.format("Everything is %s", false); // "Everything is true"
   }
}

En supposant qu'aucun SecurityException est levée, le code ci-dessus imprime "Everything is true".

Ce qui est fait ici est comme suit:

  • La primitive boolean valeurs true et false en main sont autoboxed de type de référence Boolean "constantes" Boolean.TRUE et Boolean.FALSE
  • La réflexion est utilisée pour changer l' public static final Boolean.FALSE pour se référer à l' Boolean visé par l' Boolean.TRUE
  • En conséquence, par la suite, chaque fois qu'un false est autoboxed d' Boolean.FALSE, il se réfère à la même Boolean que celui référencé par Boolean.TRUE
  • Tout ce qui a été "false" maintenant est - "true"

Questions connexes


Mises en garde

Un soin extrême devraient être prises, chaque fois que vous faites quelque chose comme cela. Il ne peut pas fonctionner, car SecurityManager peut être présent, mais même si elle ne le fait pas, selon le mode d'utilisation, il peut ou peut ne pas fonctionner.

JLS 17.5.3 Modification Ultérieure de Finale Champs

Dans certains cas, tels que la désérialisation, le système aura besoin de changer l' final les champs d'un objet après la construction. final champs peuvent être modifiés à l'aide de la réflexion et autres dépendant de l'implémentation des moyens. Le seul modèle de ce qui est raisonnable, la sémantique est l'une dans laquelle un objet est construit, puis l' final des champs de l'objet sont mis à jour. L'objet ne doit pas être visible pour les autres threads, ni l' final des champs de lire, jusqu'à ce que toutes les mises à jour à l' final des champs de l'objet sont terminées. Se fige d'un final champ se produire à la fois à la fin du constructeur dans lequel l' final champ est défini, et immédiatement après chaque modification d'un final domaine par la réflexion ou l'autre mécanisme spécial.

Même alors, il ya un certain nombre de complications. Si un final champ est initialisé à une constante de compilation dans le domaine de déclaration, de modifications à l' final champ peut ne pas être observé, depuis les utilisations de l' final domaine sont remplacés au moment de la compilation avec la constante de compilation.

Un autre problème est que la spécification permet agressives d'optimisation de l' final champs. Dans un thread, il est permis de réorganiser lit d'une final champ avec ces modifications de finale de terrain qui n'ont pas lieu dans le constructeur.

Voir aussi

  • JLS 15.28 Expression Constante
    • Il est peu probable que cette technique fonctionne avec une primitive private static final boolean, parce que c'est inlineable comme une constante de compilation et donc le "nouveau" valeur peut ne pas être observables

Annexe: Sur le bit-à-bit de manipulation

Essentiellement,

field.getModifiers() & ~Modifier.FINAL

désactive le bit correspondant à Modifier.FINAL de field.getModifiers(). & est le niveau du bit et, et ~ est le bit à bit-complément de.

Voir aussi

69voto

erickson Points 127945

Si la valeur attribuée à un static final boolean champ est connu à la compilation, c'est une constante. Sa valeur est insérée dans le code qui fait référence à la valeur. Puisqu'il n'est pas réellement lu au moment de l'exécution, la modification puis, il n'aura aucun effet.

La Java language specification dit ceci:

Si un champ est une constante variable (§4.12.4), puis en supprimant le mot-clé finale ou en changeant sa valeur ne sera pas casser la compatibilité avec les pré-existant les fichiers binaires en les obligeant à ne pas courir, mais ils ne voient pas tous nouvelle valeur pour l'utilisation du terrain, à moins qu'ils sont recompilées. Cela est vrai même si l'utilisation elle-même n'est pas au moment de la compilation expression constante (§15.28)

Voici un exemple:

class Flag {
  static final boolean FLAG = true;
}

class Checker {
  public static void main(String... argv) {
    System.out.println(Flag.FLAG);
  }
}

Si vous décompiler Checker, vous verrez qu'au lieu de référencer Flag.FLAG, le code pousse simplement une valeur de 1 (true) sur la pile (instruction n ° 3).

0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3:   iconst_1
4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
7:   return

20voto

Un peu de curiosité de la Java Language Specification, chapitre 17, section 17.5.4 "protégé en Écriture Champs":

Normalement, un domaine qui est finale et statique ne peut pas être modifié. Cependant, Le Système De.dans, Système.out et System.err sont statiques final champs que, pour des raisons d'héritage, doivent être autorisés à être modifié par les méthodes Système.setIn, Système.sera, et du Système.setErr. Nous nous référons à ces les champs comme étant protégé en écriture afin de les distinguer de l'ordinaire final champs.

Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4

-5voto

thecoop Points 23695

Le point entier d'un champ final 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, des classes internes référençant des variables externes). Donc non. Etre capable de le faire casserait la JVM!

La solution n'est pas de le déclarer final en premier lieu.

-6voto

aizamzanee Points 1

Ses

 public static final Boolean FALSE=new Boolean(false)  
 

ne pas

 public static final boolean FALSE=false
 

doit être déclaré:-

 public final Boolean FALSE=new Boolean(false)
public final boolean FALSE=false
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X