32

Can the value of a Java static final class variable be retrieved through reflection?

wavicle
  • 1,284
  • 1
  • 10
  • 12

4 Answers4

55

I would guess that it depends on the type and the compiler (on second thought, it had sure better not!). Sun's compiler inlines primitive constants, but I don't know if they remove the entry from the class entirely. I'll find out.

Edit: Yes, you can still access them even if they are inlined. Test class:

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));
        }
    }
}

Output:

CONST_INT: 100
CONST_STRING: String
CONST_OBJECT: xyz

javap -c output:

Compiled from "ReflectionConstantTest.java"
public class scratch.ReflectionConstantTest extends java.lang.Object{
public scratch.ReflectionConstantTest();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   bipush  100
   2:   istore_1
   3:   ldc     #2; //String String
   5:   astore_2
   6:   getstatic       #3; //Field CONST_OBJECT:Ljava/lang/Object;
   9:   astore_3
   10:  ldc_w   #4; //class scratch/ReflectionConstantTest
   13:  invokevirtual   #5; //Method java/lang/Class.getDeclaredFields:()[Ljava/lang/reflect/Field;
   16:  astore  4
   18:  aload   4
   20:  arraylength
   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:  astore  7
   40:  aload   7
   42:  iconst_1
   43:  invokevirtual   #6; //Method java/lang/reflect/Field.setAccessible:(Z)V
   46:  getstatic       #7; //Field java/lang/System.out:Ljava/io/PrintStream;
   49:  new     #8; //class java/lang/StringBuilder
   52:  dup
   53:  invokespecial   #9; //Method java/lang/StringBuilder."":()V
   56:  aload   7
   58:  invokevirtual   #10; //Method java/lang/reflect/Field.getName:()Ljava/lang/String;
   61:  invokevirtual   #11; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   64:  ldc     #12; //String :
   66:  invokevirtual   #11; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   69:  aload   7
   71:  aconst_null
   72:  invokevirtual   #13; //Method java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
   75:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
   78:  invokevirtual   #15; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   81:  invokevirtual   #16; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   84:  iinc    6, 1
   87:  goto    26
   90:  return

static {};
  Code:
   0:   new     #8; //class java/lang/StringBuilder
   3:   dup
   4:   ldc     #17; //String xyz
   6:   invokespecial   #18; //Method java/lang/StringBuilder."":(Ljava/lang/String;)V
   9:   putstatic       #3; //Field CONST_OBJECT:Ljava/lang/Object;
   12:  return

}

You can see that CONST_INT is inlined, but CONST_STRING and CONST_OBJECT (of course) are not. Yet CONST_INT is still available reflectively.

Michael Myers
  • 188,989
  • 46
  • 291
  • 292
  • This solved my problem, thanks! I'm curious as to why we have to call f.setAccessible(true). What's the point of having static modifiers not accessible in the first place? – gsgx Feb 07 '12 at 16:31
  • @gsingh2011: The only reason that's necessary is to demonstrate that even private constants can be accessed through reflection (you may observe that the three constants in the example are declared private). Public constants would not need to be set accessible, since they are already accessible. – Michael Myers Feb 07 '12 at 17:17
  • Ahh, I see. I just realized my problem occurred because I did not provide a public/private modifier to my field, and then refactored my code into packages. Thanks anyway for the response. – gsgx Feb 07 '12 at 17:28
6

Yes. (Only there is no such thing as static, instance. It's static, non-instance.)

> If the underlying field is a static field, the obj argument is ignored; it may be null.

(include standard warning that most uses of reflection are a bad idea)

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
1

If open-source libraries are allowed on your project you can use

FieldUtils.readDeclaredStaticField

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

In another class you can use:

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

You will see "myConstantValue" in the console.

paskos
  • 689
  • 8
  • 21
0

Just getting the name and value does not require setAccessible(true). Here's a useful example when you have to deal with constants declared in an interface, and want the symbolic names:

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);
}
Per Lindberg
  • 737
  • 8
  • 8