设置带有反射的私有静态final字段



基于使用Java反射更改私有静态final字段,我尝试设置私有静态final字段。

(我知道这很不礼貌,但这个问题与代码质量无关;是关于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内联final字段,这些字段在编译时初始化为常量表达式

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

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


* - JavaWorld引用说static final。Kumar指出,在常量变量的定义中,语言规范并不需要static。我认为Kumar是对的,JavaWorld是错误的。

在某些情况下,java不内联静态final常量。并且不同的Java版本之间的反射行为是不同的(例如在现代Java中获取modifiers字段将失败),但是有些东西在Java版本之间保持一致- sun.misc.Unsafe

您可以使用它来修改任何字段的值,并绕过所有的访问修饰符。

    public static void setStaticObjectUnsafe(final Field field, Object value) {
        final Object staticFieldBase = theUnsafe.staticFieldBase(field);
        final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
        theUnsafe.putObject(staticFieldBase, staticFieldOffset, value);
    }

对于原语,它是相同的,除了你需要替换value参数和putObject与它的原语类型的替代:

    public static void setStaticIntegerUnsafe(final Field field, int value) {
        final Object staticFieldBase = theUnsafe.staticFieldBase(field);
        final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
        theUnsafe.putInt(staticFieldBase, staticFieldOffset, value);
    }

要获得不安全的实例,请参阅此解决方案。

如果你使用的是Eclipse IDE,你需要在首选项中调整编译器设置:Java→编译器→错误/警告→设置"禁止引用(访问规则)";忽略

相关内容

  • 没有找到相关文章

最新更新