web-dev-qa-db-fra.com

Accès à la valeur de la variable finale statique Java par réflexion

La valeur d'une variable de classe finale statique Java peut-elle être extraite par réflexion?

32
Dave Bouvier

Je suppose que cela dépend du type et du compilateur (à la réflexion, il valait mieux ne pas!). Les constantes primitives en ligne du compilateur de Sun, mais je ne sais pas si elles suppriment entièrement l'entrée de la classe. Je trouverai.

Edit: Oui, vous pouvez toujours y accéder même s’ils sont en ligne. Classe de test:

public class ReflectionConstantTest {
    private static final int CONST_INT = 100;
    private static final String CONST_STRING = "String";
    private static final Object CONST_OBJECT = new StringBuilder("xyz");
    public static void main(String[] args) throws Exception {
        int testInt = CONST_INT;
        String testString = CONST_STRING;
        Object testObj = CONST_OBJECT;
        for (Field f : ReflectionConstantTest.class.getDeclaredFields()) {
            f.setAccessible(true);
            System.out.println(f.getName() + ": " + f.get(null));
        }
    }
}

Sortie:

 CONST_INT: 100 
 CONST_STRING: Chaîne 
 CONST_OBJECT: xyz 

javap -c sortie:

 Compilé à partir de "ReflectionConstantTest.Java" 
 La classe publique scratch.ReflectionConstantTest étend Java.lang.Object {
 Public scratch.ReflectionConstantTest (); 
 Code:
 0: aload_0 
 1: invokespecial # 1; // Méthode Java/lang/Object. "" :() V 
 4: return 

 Public static void main (Java.lang.String []) lève Java.lang.Exception; 
 Code:
 0: bipush 100 
 2: istore_1 
 3: ldc # 2; // String String 
 5: astore_2 
 6: getstatic # 3; // Champ CONST_OBJECT: Ljava/lang/Object; 
 9: astore_3 
 10: ldc_w # 4; // classe scratch/ReflectionConstantTest 
 13: invokevirtual # 5; // Méthode Java/lang/Class.getDeclaredFields: () [Ljava/lang/reflect/Field; 
 16: au magasin 4 
 18: plus de 4 
 20: longueur de l'arme 
 21: istore 5 
 23: iconst_0 
 24: istore 6 
 26: iload 6 
 28: iload 5 
 30: if_icmpge 90 
 33: aload 4 
 35: iload 6 
 37: aaload 
 38: au magasin 7 
 40: environ 7 
 42: iconst_1 
 43: invokevirtual # 6; // Méthode Java/lang/reflect/Field.setAccessible: (Z) V 
 46: getstatic # 7; // Champ Java/lang/System.out: Ljava/io/PrintStream; 
 49: nouveau n ° 8; // classe Java/lang/StringBuilder 
 52: dup 
 53: invokespecial # 9; // Méthode Java/lang/StringBuilder. "" :() V 
 56: plus de 7 
 58: invokevirtual # 10; // Méthode Java/lang/reflect/Field.getName :() Ljava/lang/String; 
 61: invokevirtual # 11; // Méthode Java/lang/StringBuilder.append: (Ljava/lang/String;) Ljava/lang/StringBuilder; 
 64: ldc # 12; //Chaîne :
 66: invokevirtual # 11; // Méthode Java/lang/StringBuilder.append: (Ljava/lang/String;) Ljava/lang/StringBuilder; 
 69: environ 7 
 71: aconst_null 
 72: invokevirtual # 13; // Méthode Java/lang/reflect/Field.get: (Ljava/lang/Object;) Ljava/lang/Object; 
 75: invokevirtual # 14; // Méthode Java/lang/StringBuilder.append: (Ljava/lang/Object;) Ljava/lang/StringBuilder; 
 78: invokevirtual # 15; // Méthode Java/lang/StringBuilder.toString :() Ljava/lang/String; 
 81: invokevirtual # 16; // Méthode Java/io/PrintStream.println: (Ljava/lang/String;) V 
 84: Iinc 6, 1 
 87: passez à 26 
 90: retour 

 Static {}; 
 Code:
 0: nouveau # 8; // classe Java/lang/StringBuilder 
 3: dup 
 4: ldc # 17; // String xyz 
 6: invokespecial # 18; // Méthode Java/lang/StringBuilder. "" :( Ljava/lang/String;) V 
 9: putstatic # 3; // Champ CONST_OBJECT: Ljava/lang/Object; 
 12: retour 

} 

Vous pouvez voir que CONST_INT est en ligne, mais CONST_STRING et CONST_OBJECT (bien sûr) ne le sont pas. Pourtant, CONST_INT est toujours disponible de manière réfléchie.

55
Michael Myers

Oui. (Seulement, il n’existe pas de statique, instance. C’est statique, non-instance.)

> Si le champ sous-jacent est un champ statique, l'argument obj est ignoré. il peut être nul.

(inclure un avertissement standard indiquant que la plupart des utilisations de la réflexion sont une mauvaise idée)

6

Si les bibliothèques open-source sont autorisées sur votre projet, vous pouvez utiliser 

FieldUtils.readDeclaredStaticField

public class Test {
    public final static String CONSTANT="myConstantValue";
}

Dans une autre classe, vous pouvez utiliser:

Object value = FieldUtils.readDeclaredStaticField(Test.class, "CONSTANT");
System.out.println(value);

Vous verrez "myConstantValue" dans la console.

1
paskos

Obtenir le nom et la valeur ne nécessite pas setAccessible (true). Voici un exemple utile lorsque vous devez gérer des constantes déclarées dans une interface et que vous voulez les noms symboliques:

interface Code {
   public static final int FOO = 0;
   public static final int BAR = 1;
}

...

try {
   for (Field field : Code.class.getDeclaredFields()) {
      String name = field.getName();
      int value = field.getInt(null);
      System.out.println(name + "=" + value);
   }
}
catch (IllegalAccessException e) {
   System.out.println(e);
}
0
Per Lindberg