【问题标题】:Setting private static final field with reflection使用反射设置私有静态最终字段
【发布时间】:2014-10-13 23:37:33
【问题描述】:

基于Change private static final field using Java reflection,我尝试设置一个私有静态final字段。

(我知道这非常 hacky,但这个问题与代码质量无关;它与 Java 反射有关。)

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

class Main {

  static class Foo {
    private static final int A = 1;

    int getA() {
      return A;
    }
  }

  public static void main(String[] args) throws Exception {
    Field modifiers = Field.class.getDeclaredField("modifiers");
    modifiers.setAccessible(true);

    Field field = Foo.class.getDeclaredField("A");
    field.setAccessible(true);
    modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, 2);

    System.out.println(new Foo().getA()); // should print 2
  }

}

打印出来

1

我已经在 OpenJDK 6 和 7 以及 Oracle 7 上尝试过。

我不知道 Java 反射提供了什么保证。但是如果它失败了,我认为会有一个Exception(实际上所有的反射方法都会抛出异常)。

这里发生了什么?

【问题讨论】:

标签: java reflection


【解决方案1】:

在编译时初始化为 constant expressions 的 Java inlines final fields

根据 Java 语言规范,使用可在编译时计算的表达式初始化的任何 static final* 字段必须编译为“内联”字段值的字节码。也就是说,Main 类中不会出现动态链接,告诉它在运行时从InterfaceA 获取A 的值。

反编译字节码,你会发现getA()的主体只是简单的压入常量1并返回。


* - JavaWorld 引用说static final。 Kumar 指出,language specification常量变量 的定义中不需要static。我认为 Kumar 是对的,而 JavaWorld 是错误的。

【讨论】:

  • +1 OP,它没有失败。试试System.out.println(field.get(null)); // should print 2
  • @SotiriosDelimanolis,这是阐明解释器行为的绝佳测试。
  • @MikeSamuel 我认为它应该适用于使用可以在编译时评估的表达式初始化的任何最终变量,静态不是必需的
  • @KumarAbhinav,我认为你是对的:JLS 4.12.4 说“原始类型的变量或String 类型的变量,即final 并使用编译时常量表达式初始化(​​§ 15.28),被称为常量变量”,没有任何对static的引用。
  • @PaulDraper,很好。我没有看到在构造函数外部发生的对 final 字段的读取和写入重新排序的警告。
猜你喜欢
  • 2011-03-19
  • 2012-06-26
  • 2012-09-29
  • 2012-03-12
  • 2011-05-29
  • 2015-04-12
  • 2015-08-22
相关资源
最近更新 更多