我有一个带有private static final字段的类,不幸的是,我需要在运行时进行更改。

使用反射我得到此错误: java.lang.IllegalAccessException: Can not set static final boolean field

有什么办法可以改变价值?

Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);

#1楼

Java语言规范第17章,第17.5.4节“写保护字段”:

通常,不得修改final和static字段。 但是,System.in,System.out和System.err是静态的最终字段,由于遗留原因,必须允许使用System.setIn,System.setOut和System.setErr方法进行更改。 我们称这些字段为写保护的,以区别于普通的最终字段。

来源: http : //docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4


#2楼

我也将它与joor库集成在一起

只需使用

      Reflect.on(yourObject).set("finalFieldName", finalFieldValue);

我还解决了以前的解决方案似乎override问题。 但是,只有在没有其他好的解决方案时,才应谨慎使用此功能。


#3楼

final字段的全部要点是,一旦设置,就不能重新分配它。 JVM使用此保证人在各个地方保持一致性(例如,内部类引用外部变量)。 所以不行。 这样做会破坏JVM!

解决方案是首先不要将其声明为final


#4楼

假设没有SecurityManager阻止您执行此操作,则可以使用setAccessible避开private并重置修饰符以摆脱final ,并实际上修改private static final字段。

这是一个例子:

import java.lang.reflect.*;public class EverythingIsTrue {static void setFinalStatic(Field field, Object newValue) throws Exception {field.setAccessible(true);Field modifiersField = Field.class.getDeclaredField("modifiers");modifiersField.setAccessible(true);modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);field.set(null, newValue);}public static void main(String args[]) throws Exception {      setFinalStatic(Boolean.class.getField("FALSE"), true);System.out.format("Everything is %s", false); // "Everything is true"}
}

假设没有抛出SecurityException ,则上面的代码将显示"Everything is true"

实际执行的操作如下:

  • main中的原始booleantruefalse装箱以引用类型Boolean “ constants” Boolean.TRUEBoolean.FALSE
  • 反思是用来改变public static final Boolean.FALSE来指代Boolean被称为Boolean.TRUE
  • 其结果是,随后每当false被autoboxed到Boolean.FALSE ,它指的是相同的Boolean作为一个由refered到Boolean.TRUE
  • 现在所有"false""true"

相关问题

  • 使用反射更改static final File.separatorChar以进行单元测试
  • 如何将setAccessible限制为仅“合法”使用?
    • 有使Integer的缓存混乱,使String突变等的示例

注意事项

每当您执行此类操作时,都应格外小心。 由于可能存在SecurityManager因此它可能不起作用,但是即使不起作用,根据使用模式的不同,它也可能起作用或不起作用。

JLS 17.5.3最终字段的后续修改

在某些情况下,例如反序列化,系统将需要在构造后更改对象的final字段。 可以通过反射和其他依赖于实现的方式来更改final字段。 具有合理语义的唯一模式是构造一个对象,然后更新该对象的final字段的模式。 对象不应该是可见的其他线程,也不应final场被读,直到所有更新final目标领域是完整的。 final字段的冻结既发生在设置了final字段的构造函数的末尾,也发生在通过反射或其他特殊机制对final字段进行的每次修改之后。

即使这样,仍然存在许多并发症。 如果final字段被初始化为在字段声明一个编译时间常数,改变到final字段可以不被观察到,因为该用途final场是在编译时与编译时间常数替代。

另一个问题是,该规范允许对final字段进行积极的优化。 在线程内,可以使用在构造函数中不进行的对最终字段的修改来重新排序final字段的读取。

也可以看看

  • JLS 15.28常数表达式

    • 此技术不太可能与原始的private static final boolean ,因为它可以作为编译时常量内联,因此“ new”值可能无法观察到

附录:关于按位操作

实质上,

field.getModifiers() & ~Modifier.FINAL

关闭field.getModifiers()Modifier.FINAL对应的位。 &是按位与,而~是按位补码。

也可以看看

  • 维基百科/按位操作

记住常数表达式

仍然无法解决这个问题吗?像我一样,陷入了抑郁吗? 您的代码看起来像这样吗?

public class A {private final String myVar = "Some Value";
}

阅读有关此答案的注释,特别是@Pshemo的注释,它使我想起了常量表达式的处理方式不同,因此将无法对其进行修改。 因此,您需要将代码更改为如下所示:

public class A {private final String myVar;private A() {myVar = "Some Value";}
}

如果您不是课程的所有者,我会感觉到您!

有关为什么此行为的更多详细信息,请阅读此内容 ?


#5楼

如果在编译时知道分配给static final boolean字段的值,则它是一个常数。 原始类型或String类型的字段可以是编译时常量。 在引用该字段的任何代码中都将内联一个常量。 由于在运行时实际上并未读取该字段,因此对其进行更改将无效。

Java语言规范说:

如果字段是常量变量(第4.12.4节),则删除关键字final或更改其值不会导致不运行而破坏与现有二进制文件的兼容性,但它们不会看到任何新的用法值除非重新编译它们。 即使用法本身不是编译时常量表达式(第15.28节),也是如此。

这是一个例子:

class Flag {static final boolean FLAG = true;
}class Checker {public static void main(String... argv) {System.out.println(Flag.FLAG);}
}

如果您对Checker反编译,则会看到它没有引用Flag.FLAG ,而是简单地将值1( true )压入堆栈(指令3)。

0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3:   iconst_1
4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
7:   return

#6楼

只是在面试问题中看到了一个问题,如果可能的话,可以通过反射或在运行时更改最终变量。 真的很感兴趣,所以我开始变得:

 /*** @author Dmitrijs Lobanovskis* @since 03/03/2016.*/
public class SomeClass {private final String str;SomeClass(){this.str = "This is the string that never changes!";}public String getStr() {return str;}@Overridepublic String toString() {return "Class name: " + getClass() + " Value: " + getStr();}
}

一些带有最终String变量的简单类。 因此,在主类中导入java.lang.reflect.Field;。

/*** @author Dmitrijs Lobanovskis* @since 03/03/2016.*/
public class Main {public static void main(String[] args) throws Exception{SomeClass someClass = new SomeClass();System.out.println(someClass);Field field = someClass.getClass().getDeclaredField("str");field.setAccessible(true);field.set(someClass, "There you are");System.out.println(someClass);}
}

输出将如下所示:

Class name: class SomeClass Value: This is the string that never changes!
Class name: class SomeClass Value: There you areProcess finished with exit code 0

根据文档https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html


#7楼

如果存在安全管理器,则可以使用AccessController.doPrivileged

从上面接受的答案中拿相同的例子:

import java.lang.reflect.*;public class EverythingIsTrue {static void setFinalStatic(Field field, Object newValue) throws Exception {field.setAccessible(true);Field modifiersField = Field.class.getDeclaredField("modifiers");// wrapping setAccessible AccessController.doPrivileged(new PrivilegedAction() {@Overridepublic Object run() {modifiersField.setAccessible(true);return null;}});modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);field.set(null, newValue);}public static void main(String args[]) throws Exception {      setFinalStatic(Boolean.class.getField("FALSE"), true);System.out.format("Everything is %s", false); // "Everything is true"}
}

在lambda表达式中, AccessController.doPrivileged可以简化为:

AccessController.doPrivileged((PrivilegedAction) () -> {modifiersField.setAccessible(true);return null;
});

#8楼

在将其部署到JDK 1.8u91上之前,可接受的答案一直对我有用。 然后我意识到它在field.set(null, newValue);失败了field.set(null, newValue); 在调用setFinalStatic方法之前通过反射读取值的setFinalStatic

也许读引起的Java反射内部有些不同设置(即sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl中,而不是失败的情况下sun.reflect.UnsafeStaticObjectFieldAccessorImpl在成功的情况下),但我并没有进一步阐述它。

由于我需要根据旧值临时设置新值,然后再将旧值重新设置,因此我做了一点点改动以在外部提供计算功能,并返回旧值:

public static <T> T assignFinalField(Object object, Class<?> clazz, String fieldName, UnaryOperator<T> newValueFunction) {Field f = null, ff = null;try {f = clazz.getDeclaredField(fieldName);final int oldM = f.getModifiers();final int newM = oldM & ~Modifier.FINAL;ff = Field.class.getDeclaredField("modifiers");ff.setAccessible(true);ff.setInt(f,newM);f.setAccessible(true);T result = (T)f.get(object);T newValue = newValueFunction.apply(result);f.set(object,newValue);ff.setInt(f,oldM);return result;} ...

但是,对于一般情况,这还不够。


#9楼

除了排名靠前的答案,您还可以使用一些简单的方法。 Apache commons FieldUtils类已经具有可以执行此操作的特定方法。 请看看FieldUtils.removeFinalModifier方法。 您应该指定目标字段实例和可访问性强制标志(如果您使用非公共字段)。 您可以在此处找到更多信息。


#10楼

如果您的字段只是私有的,则可以执行以下操作:

MyClass myClass= new MyClass();
Field aField= myClass.getClass().getDeclaredField("someField");
aField.setAccessible(true);
aField.set(myClass, "newValueForAString");

并抛出/处理NoSuchFieldException


#11楼

即使是final字段,也可以在静态初始化程序之外修改字段,并且(至少JVM HotSpot)可以完美执行字节码。

问题是Java编译器不允许这样做,但是可以使用objectweb.asm轻松绕过objectweb.asm 。 这是通过字节码验证并在JVM HotSpot OpenJDK12下成功加载和初始化的完全有效的类文件:

ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Cl", null, "java/lang/Object", null);
{FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "fld", "I", null, null);fv.visitEnd();
}
{// public void setFinalField1() { //... }MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "setFinalField1", "()V", null, null);mv.visitMaxs(2, 1);mv.visitInsn(Opcodes.ICONST_5);mv.visitFieldInsn(Opcodes.PUTSTATIC, "Cl", "fld", "I");mv.visitInsn(Opcodes.RETURN);mv.visitEnd();
}
{// public void setFinalField2() { //... }MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "setFinalField2", "()V", null, null);mv.visitMaxs(2, 1);mv.visitInsn(Opcodes.ICONST_2);mv.visitFieldInsn(Opcodes.PUTSTATIC, "Cl", "fld", "I");mv.visitInsn(Opcodes.RETURN);mv.visitEnd();
}
cw.visitEnd();

在Java中,该类看起来大致如下:

public class Cl{private static final int fld;public static void setFinalField1(){fld = 5;}public static void setFinalField2(){fld = 2;}
}

不能使用javac进行编译,但是可以由JVM加载和执行。

JVM HotSpot对此类有特殊的对待,因为它可以防止此类“常量”参与常量折叠。 该检查是在类初始化的字节码重写阶段完成的 :

// Check if any final field of the class given as parameter is modified
// outside of initializer methods of the class. Fields that are modified
// are marked with a flag. For marked fields, the compilers do not perform
// constant folding (as the field can be changed after initialization).
//
// The check is performed after verification and only if verification has
// succeeded. Therefore, the class is guaranteed to be well-formed.
InstanceKlass* klass = method->method_holder();
u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1);
constantPoolHandle cp(method->constants());
Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index));
if (klass->name() == ref_class_name) {Symbol* field_name = cp->name_ref_at(bc_index);Symbol* field_sig = cp->signature_ref_at(bc_index);fieldDescriptor fd;if (klass->find_field(field_name, field_sig, &fd) != NULL) {if (fd.access_flags().is_final()) {if (fd.access_flags().is_static()) {if (!method->is_static_initializer()) {fd.set_has_initialized_final_update(true);}} else {if (!method->is_object_initializer()) {fd.set_has_initialized_final_update(true);}}}}}
}

唯一的限制是JVM HotSpot的检查是在final领域不应该修改的是外部类final场在声明。

使用Java反射更改私有静态最终字段相关推荐

  1. java反射 面试题_使用Java反射更改私有静态最终字段

    假设没有SecurityManager阻止你执行此操作,则可以使用setAccessible来绕开private并重置修饰符以摆脱final,并实际上修改private static final字段. ...

  2. java 对象复制 反射_利用Java反射机制实现对象相同字段的复制操作

    一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档) ...

  3. java 反射父类私有属性值_如何在Java中通过反射访问父类的父类的私有字段? - java...

    在一个API中,我使用的是一个抽象类(A类),该类具有一个私有字段(A.privateField). B类在API中扩展了A类. 我需要扩展B类的实现,即C类,但是我需要A类的privateField ...

  4. java反射更改方法内容_Java反射

    类的加载 java运行的都是类 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现这个类进行初始化. 加载 加载,是指Java虚拟机查找字节流(查找.class ...

  5. java 反射 调用私有构造函数_使用反射调用私有方法

    1.创建一个被调用的类 /** * @version 1.0 * @Author F-llin * @Date 2020-12-04 13:53 */ public class Bean{ priva ...

  6. java怎么通过字段去获取对象_通过java反射获取任意对象的字段名及字段值

    import java.lang.reflect.Field; public class ReflectClass3 { /** * @param args */ public static void ...

  7. java 对象复制字段_利用Java反射机制实现对象相同字段的复制

    一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档) ...

  8. Java 反射修改类的常量值、静态变量值、属性值

    前言 有的时候,我们需要修改一个变量的值,但变量也许存在于 Jar 包中或其他位置,导致我们不能从代码层面进行修改,于是我们就用到了下面的场景,通过反射来进行修改变量的值. 定义一个实体类 class ...

  9. java 反射获取父类的字段_java反射获取父类和子类字段值、赋值

    这里将告诉您java反射获取父类和子类字段值.赋值,具体操作过程:java反射获取字段值.赋值 import org.springframework.util.ReflectionUtils; imp ...

最新文章

  1. 【MATLAB】矩阵运算之矩阵分解
  2. 如何实现一个HTML5 RPG游戏引擎——第一章,实现地图类
  3. c#扩展方法奇思妙用高级篇七:“树”通用遍历器
  4. 纯CSS3画出小黄人并实现动画效果
  5. 【渝粤教育】电大中专市场营销管理 (2)作业 题库
  6. 如何火眼金睛鉴定那些单细胞转录组中的混杂因素
  7. PNG免扣素材|圣诞树海报素材,元素很多哦!
  8. 网络GHOST使用方法
  9. C/C++ Memory Layout
  10. VB6.0动态加载ActiveX控件漫谈[转]
  11. python 幅度和相位求复数_皮质运动兴奋性不受中央区mu节律相位的调节
  12. 笔记本合上盖子就断网怎么办?
  13. Java使用数组几个数字比较大小进行排序
  14. assign和weak之间的区别
  15. 实战 J2EE 开发购物网站 开发环境篇
  16. 什么副业来钱快?有什么靠谱的副业可以做?
  17. PSPad v4.5.3(2295) Beta
  18. 介绍家乡的html源代码_APICloud Studio3.0最新功能及使用流程介绍
  19. 01.一个自动合并多个excel表(非多工作簿)的工具
  20. 进程1的创建__父子进程创建机制

热门文章

  1. android 事件拦截 (Viewpager不可以左右滑动)
  2. android stadio svn 使用技巧
  3. Android 布局开发之百分比布局、弹性布局
  4. 第十、十一周项目-阅读程序,写出这些程序的运行结果(4)
  5. redis哨兵机制在集群中的应用
  6. python转cython_用Cython加速Python到“起飞”(推荐)
  7. Expanded, SingleChildScrollView, CustomScrollView, container, height, width
  8. vs2017的一件奇葩事
  9. LeetCode Minimum Moves to Equal Array Elements II
  10. rails 数据库相关操作命令