【发布时间】:2015-09-10 10:28:28
【问题描述】:
有什么方法可以使用反射来确定静态最终字段是否具有“=”值(以便在访问时内联该值)或者其值是否由静态初始化程序分配? 如果该字段确实有一个值,是否有任何方法可以在不使用 Field.get 方法的情况下检索该值(并因此导致执行静态初始化程序)?
【问题讨论】:
标签: java reflection field
有什么方法可以使用反射来确定静态最终字段是否具有“=”值(以便在访问时内联该值)或者其值是否由静态初始化程序分配? 如果该字段确实有一个值,是否有任何方法可以在不使用 Field.get 方法的情况下检索该值(并因此导致执行静态初始化程序)?
【问题讨论】:
标签: java reflection field
只有当它是编译时常量时,该字段才会被“内联” - 请参阅JLS 中的冗长定义。
因此在声明时分配它是不够的。 这不是一个编译时常量,虽然是一个有效的声明:
static final int INT_CONST = compute();
static int compute() {
return 5;
}
仅通过反射,您无法确定该字段是如何初始化的。
要访问类字段的任何值,需要先加载该类,因此您无法阻止静态初始化程序运行。
【讨论】:
你不能通过反射来做到这一点,但你可以使用像ASM这样的字节码工程库来做到这一点:
public class AsmTest {
static final int a = 2; // constant
static final String b = "string"; // constant
static final String c = "foo "+"bar"; // constant: concatenation is allowed
static final String d = " foobar ".trim(); // not constant: method called
public static Object getFieldConstantValue(Class<?> clazz, final String field) {
try(InputStream is = clazz.getResourceAsStream(clazz.getSimpleName()+".class")) {
final Object[] value = {null};
new ClassReader(is).accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public FieldVisitor visitField(int access, String name, String desc,
String sig, Object val) {
if(name.equals(field))
value[0] = val;
return null;
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return value[0];
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
for(String name : new String[] {"a", "b", "c", "d"}) {
System.out.println(name+"="+getFieldConstantValue(AsmTest.class, name));
}
}
}
输出:
a=2
b=string
c=foo bar
d=null
【讨论】:
byte[] 数组。这样它就可以读取任何类,即使它根本没有加载到您的应用程序中。