使用Java反射更改私有静态最终字段
我有一个带有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
中的原始boolean
值true
和false
装箱以引用类型Boolean
“ constants”Boolean.TRUE
和Boolean.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反射更改私有静态最终字段相关推荐
- java反射 面试题_使用Java反射更改私有静态最终字段
假设没有SecurityManager阻止你执行此操作,则可以使用setAccessible来绕开private并重置修饰符以摆脱final,并实际上修改private static final字段. ...
- java 对象复制 反射_利用Java反射机制实现对象相同字段的复制操作
一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档) ...
- java 反射父类私有属性值_如何在Java中通过反射访问父类的父类的私有字段? - java...
在一个API中,我使用的是一个抽象类(A类),该类具有一个私有字段(A.privateField). B类在API中扩展了A类. 我需要扩展B类的实现,即C类,但是我需要A类的privateField ...
- java反射更改方法内容_Java反射
类的加载 java运行的都是类 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现这个类进行初始化. 加载 加载,是指Java虚拟机查找字节流(查找.class ...
- java 反射 调用私有构造函数_使用反射调用私有方法
1.创建一个被调用的类 /** * @version 1.0 * @Author F-llin * @Date 2020-12-04 13:53 */ public class Bean{ priva ...
- java怎么通过字段去获取对象_通过java反射获取任意对象的字段名及字段值
import java.lang.reflect.Field; public class ReflectClass3 { /** * @param args */ public static void ...
- java 对象复制字段_利用Java反射机制实现对象相同字段的复制
一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档) ...
- Java 反射修改类的常量值、静态变量值、属性值
前言 有的时候,我们需要修改一个变量的值,但变量也许存在于 Jar 包中或其他位置,导致我们不能从代码层面进行修改,于是我们就用到了下面的场景,通过反射来进行修改变量的值. 定义一个实体类 class ...
- java 反射获取父类的字段_java反射获取父类和子类字段值、赋值
这里将告诉您java反射获取父类和子类字段值.赋值,具体操作过程:java反射获取字段值.赋值 import org.springframework.util.ReflectionUtils; imp ...
最新文章
- 【MATLAB】矩阵运算之矩阵分解
- 如何实现一个HTML5 RPG游戏引擎——第一章,实现地图类
- c#扩展方法奇思妙用高级篇七:“树”通用遍历器
- 纯CSS3画出小黄人并实现动画效果
- 【渝粤教育】电大中专市场营销管理 (2)作业 题库
- 如何火眼金睛鉴定那些单细胞转录组中的混杂因素
- PNG免扣素材|圣诞树海报素材,元素很多哦!
- 网络GHOST使用方法
- C/C++ Memory Layout
- VB6.0动态加载ActiveX控件漫谈[转]
- python 幅度和相位求复数_皮质运动兴奋性不受中央区mu节律相位的调节
- 笔记本合上盖子就断网怎么办?
- Java使用数组几个数字比较大小进行排序
- assign和weak之间的区别
- 实战 J2EE 开发购物网站 开发环境篇
- 什么副业来钱快?有什么靠谱的副业可以做?
- PSPad v4.5.3(2295) Beta
- 介绍家乡的html源代码_APICloud Studio3.0最新功能及使用流程介绍
- 01.一个自动合并多个excel表(非多工作簿)的工具
- 进程1的创建__父子进程创建机制
热门文章
- android 事件拦截 (Viewpager不可以左右滑动)
- android stadio svn 使用技巧
- Android 布局开发之百分比布局、弹性布局
- 第十、十一周项目-阅读程序,写出这些程序的运行结果(4)
- redis哨兵机制在集群中的应用
- python转cython_用Cython加速Python到“起飞”(推荐)
- Expanded, SingleChildScrollView, CustomScrollView, container, height, width
- vs2017的一件奇葩事
- LeetCode Minimum Moves to Equal Array Elements II
- rails 数据库相关操作命令