Java 包最终变量可以通过反射进行更改吗?

Java 包最终变量可以通过反射进行更改吗?,java,access-modifiers,Java,Access Modifiers,包最终变量可以通过反射进行更改吗 假设我有这个: public class Widget { final int val = 23; } 如果可以访问,可以通过反射更改val吗 如果是这样,是否有任何方法可以防止在不使用安全管理器的情况下发生这种情况?是。请尝试以下代码: public static void main(String[] args) throws Exception { Widget w = new Widget (); Field m = Widget.

包最终变量可以通过反射进行更改吗

假设我有这个:

public class Widget {
  final int val = 23;
}
如果可以访问,可以通过反射更改val吗


如果是这样,是否有任何方法可以防止在不使用安全管理器的情况下发生这种情况?

是。请尝试以下代码:

public static void main(String[] args) throws Exception {
    Widget w = new Widget ();

    Field m = Widget.class.getDeclaredField("val");

    m.setAccessible(true);

    m.set(w, 233);

    System.out.println(m.get(w)); /// PRINT 233
}
试试这个

 Widget() {
     checkPerMission();
  }
     private void checkPerMission() {
         Class self = sun.reflect.Reflection.getCallerClass(1);
          Class caller = sun.reflect.Reflection.getCallerClass(3);
         if (self != caller) {
             throw new java.lang.IllegalAccessError();
         }

 }

事实证明,更改最终成员会导致反射获得的值与常规代码返回的值不同!这很可怕

import java.lang.reflect.Field;

public class Test {

    private static final class Widget {
        private final int val = 23;

        public int getVal() {
            return val;
        }
    }

    public static void main(String[] args) throws Exception {
        Widget w = new Widget ();

        Field m = Widget.class.getDeclaredField("val");

        m.setAccessible(true);

        m.set(w, 233);

        Field m1 = Widget.class.getDeclaredField("val");
        m1.setAccessible(true);


        System.out.println(m.get(w)); /// PRINT 233
        System.out.println(w.getVal()); /// PRINT 23
        System.out.println(m1.get(w)); /// PRINT 233

    }
}

帮助您如果是这样,是否有任何方法可以防止在不使用安全管理器的情况下发生这种情况?这是一项要求,还是您只是在进行实验,以确保运行时代码是可内联的,所以这是一项要求。请尝试我在下面提到的一种方法,但是
System.out.println(w.val)显示
23
,而不是
233
。所以除非你是通过反射来访问…。@T.J.Crowder cool。好的,那这是怎么回事?为什么是字段而不是getter?@T.J.Crowder:你说得对!开发人员需要意识到这一点@SaintHill:我的猜测是,因为成员是
final
,编译器或JIT能够内联该值,因为它实际上是一个常量(除非人们通过反射来处理它:-))。我不知道细节,是的。如果您使最后一个字段不是编译时常量(非字符串非原语),它将显示在实际对象中。请在将其设置为否定之前添加注释这很奇怪!我们需要进一步研究
final int val=23定义编译时常量。在编译时,对它的每次访问都会被常量值替换。编译时常量不需要是
静态的
,尽管在
小部件
的每个对象实例中为运行时从不读取的字段(除了使用反射时)保留空间是无用的。