通过反射访问Java静态final变量的值

32

Java静态final类变量的值能通过反射获取吗?

4个回答

55

我猜这取决于类型和编译器(经过重新考虑,最好不要!)Sun的编译器会内联原始常量,但我不知道它们是否从类中完全删除该条目。我会查明。

编辑:是的,即使它们被内联,你仍然可以访问它们。测试类:

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

输出:

CONST_INT: 100
CONST_STRING: String
CONST_OBJECT: xyz

javap -c 输出:

从 "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
}

你可以看到,CONST_INT 被内联,但 CONST_STRINGCONST_OBJECT 没有(当然)被内联。然而,CONST_INT 仍然可以通过反射访问。


这解决了我的问题,谢谢!我很好奇为什么我们必须调用f.setAccessible(true)。首先,静态修饰符不可访问的意义是什么? - gsgx
@gisngh2011:唯一需要这样做的原因是为了证明即使是私有常量也可以通过反射访问(您可以观察到示例中的三个常量都声明为私有)。公共常量不需要设置可访问性,因为它们已经是可访问的。 - Michael Myers
啊,我明白了。我刚刚意识到我的问题是因为我没有为我的字段提供公共/私有修饰符,然后将我的代码重构成包。无论如何,还是谢谢您的回复。 - gsgx

6

1
如果您的项目允许使用开源库,您可以使用。

FieldUtils.readDeclaredStaticField

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

在另一个类中,您可以使用以下内容:

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

您将在控制台中看到“myConstantValue”。

0

仅获取名称和值并不需要 setAccessible(true)。以下是一个有用的例子,当您必须处理在接口中声明的常量并想要符号名称时:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接