当前位置: 首页 > 面试题库 >

通过反射改变最终变量,为什么静态和非静态最终变量之间存在差异

汪修诚
2023-03-14
问题内容

请参考下面的代码。运行代码时,我可以更改最终非静态变量的值。但是,如果我尝试更改最终静态变量的值,则会抛出异常java.lang.IllegalAccessException

我的问题是,为什么在非静态最终变量也不会抛出异常,反之亦然。为什么会有所不同?

import java.lang.reflect.Field;
import java.util.Random;

public class FinalReflection {

    final static int stmark =  computeRandom();
    final int inmark = computeRandom();

    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        FinalReflection obj = new FinalReflection();
        System.out.println(FinalReflection.stmark);
        System.out.println(obj.inmark);
        Field staticFinalField  = FinalReflection.class.getDeclaredField("stmark");
        Field instanceFinalField  = FinalReflection.class.getDeclaredField("inmark");
        staticFinalField.setAccessible(true);
        instanceFinalField.setAccessible(true);

        instanceFinalField.set(obj, 100);
        System.out.println(obj.inmark);

        staticFinalField.set(FinalReflection.class, 101);
        System.out.println(FinalReflection.stmark);

    }

    private static int computeRandom() {
        return new Random().nextInt(5);
    }
}

问题答案:
FinalReflectionobj = new FinalReflection();
System.out.println(FinalReflection.stmark);
System.out.println(obj.inmark);
Field staticFinalField  = FinalReflection.class.getDeclaredField("stmark");
Field instanceFinalField  = FinalReflection.class.getDeclaredField("inmark");
staticFinalField.setAccessible(true);
instanceFinalField.setAccessible(true);

//EXTRA CODE
//Modify the final using reflection
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(staticFinalField, staticFinalField.getModifiers() & ~Modifier.FINAL);


instanceFinalField.set(obj, 100);
System.out.println(obj.inmark);
staticFinalField.set(FinalReflection.class, 101);
System.out.println(FinalReflection.stmark);

该解决方案并非没有缺点,它可能无法在所有情况下都有效:

如果final在字段声明中将字段初始化为编译时常量,则对该字段的更改final可能不可见,因为该最终字段的使用会在编译时用编译时常量替换。

另一个问题是,该规范允许对final字段进行积极的优化。在线程内,可以final使用final在构造函数中不进行的对字段的那些修改来重新排序对字段的读取。
更多关于这也是在这个类似的问题解释。



 类似资料:
  • 问题内容: 通常,最终静态成员,尤其是变量(或静态最终变量,当然可以以任何顺序使用,而不会重叠含义)已广泛用于Java接口中,以定义实现类的 协议行为 ,这意味着实现该类的类(继承)接口必须包含该接口的所有成员。 我无法区分 final 和 final静态 成员。final静态成员是一个声明为final或其他东西的静态成员?在哪些特定情况下应专门使用它们? 永远不能在方法内部,静态方法内部或实例方

  • 问题内容: 我有一个带有静态变量的Java类 如何使用反射访问对象? (我有字符串。我需要访问该对象。) 问题答案: 访问静态字段的方式与普通字段完全相同,只是不需要将任何参数传递给方法(可以传递null)。 试试这个:

  • 问题内容: 在Java中,何时应使用静态非最终变量? 例如 显然,这里我们不是在谈论常量。 根据我的经验,我经常在使用单例时对它们进行辩护,但后来我最终需要拥有多个实例,这使我感到非常头痛和重构。 似乎很少在实践中使用它们。你怎么看? 问题答案: 统计信息收集可以使用非最终变量,例如,计算创建的实例数。另一方面,对于这种情况,您可能还是要使用etc,这时可能是最终的。另外,如果您要收集多个统计信息

  • 问题内容: 将变量声明为的区别是什么 要么 如果我只希望变量是局部的,并且是常量(以后不能更改)? 谢谢 问题答案: 仅仅具有预期的效果。 声明static使其成为一个类变量,使其可以使用类名进行访问

  • 问题内容: 我已经定义了一个对象并声明了一个静态变量。在该方法中,当我尝试打印实例和类变量时,两者都打印相同的值。 不是实例变量吗?它应该打印0而不是50吗? 问题答案: 不,只有一个变量-您尚未声明任何实例变量。 不幸的是,Java允许您访问静态成员,就像通过相关类型的引用访问静态成员一样。这是IMO的设计缺陷,某些IDE(例如Eclipse)允许您将其标记为警告或错误- 但这是语言的一部分。您

  • 问题内容: 为什么默认情况下接口变量是静态变量和最终变量? 问题答案: 从Philip Shaw的Java接口设计常见问题解答中: 接口变量是静态的,因为无法单独实例化Java接口。必须在没有实例的静态上下文中分配变量的值。final修饰符确保分配给接口变量的值是一个真正的常量,不能由程序代码重新分配。