JAVA反射修改常量,以及其局限(修改private final限制)
来源:互联网 发布:mysql 数据库迁移 编辑:程序博客网 时间:2024/05/20 00:12
转载:PS:不过很好奇下面的那个private属性怎么被另一个类访问到的。
注:又研究了下,发现特么应该下面的修改常量的核心代码应该是在这个类的main函数里面,坑,写清楚点撒
对如下Bean类,其中的INT_VALUE是私有静态常量
- class Bean{
- private static final Integer INT_VALUE = 100;
- }
修改常量的核心代码:
- System.out.println(Bean.INT_VALUE);
- //获取Bean类的INT_VALUE字段
- Field field = Bean.class.getField("INT_VALUE");
- //将字段的访问权限设为true:即去除private修饰符的影响
- field.setAccessible(true);
- /*去除final修饰符的影响,将字段设为可修改的*/
- Field modifiersField = Field.class.getDeclaredField("modifiers");
- modifiersField.setAccessible(true);
- modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
- //把字段值设为200
- field.set(null, 200);
- System.out.println(Bean.INT_VALUE);
以上代码输出的结果是:
100
200
说明用反射私有静态常量成功了。
方案的局限
注意到上述代码的中的静态常量类型是Integer——但是我们项目中实际需要修改的字段类型并不是包装类型Integer,而是java的基本类型int。
当把常量的类型改成int之后,
- class Bean{
- private static final int INT_VALUE = 100;//把类型由Integer改成了int
- }
在其他代码都不变的情况下,代码输出的结果竟然变成了诡异的:
100
100
而且在调试的过程中发现,在第二次输出的时候,内存中的Bean.INT_VALUE是已经变成了200,但是System.out.println(Bean.INT_VALUE)输出的结果却依然时诡异的100?!
——反射失效了吗?
又试了其他几种类型,发现这种貌似失效的情会发生在int、long、boolean以及String这些基本类型上,而如果把类型改成Integer、Long、Boolean这种包装类型,或者其他诸如Date、Object都不会出现失效的情况。
原因
经过一系列的研究、推测、搜索等过程,终于发现了原因:
对于基本类型的静态常量,JAVA在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。
参考:Modifying final fields in Java
即对于常量 public static final int maxFormatRecordsIndex = 100 ,代码
- if( index > maxFormatRecordsIndex ){
- index = maxFormatRecordsIndex ;
- }
这段代码在编译的时候已经被java自动优化成这样的:
- if( index > 100){
- index = 100;
- }
所以在INT_VALUE是int类型的时候
- System.out.println(Bean.INT_VALUE);
- //编译时会被优化成下面这样:
- System.out.println(100);
所以,自然,无论怎么修改Boolean.INT_VALUE,System.out.println(Bean.INT_VALUE)都还是会依然固执地输出100了。
——这本身是JVM的优化代码提高运行效率的一个行为,但是就会导致我们在用反射改变此常量值时出现类似不生效的错觉。
这大概是JAVA反射的一个局限吧——修改基本类型的常量时,不是太可靠。
附一下我测试时候的DEMO吧
代码
- import java.lang.reflect.Field;
- import java.lang.reflect.Modifier;
- import java.util.Date;
- public class ForClass {
- 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 {
- System.out.println(Bean.INT_VALUE);
- setFinalStatic(Bean.class.getField("INT_VALUE"), 200);
- System.out.println(Bean.INT_VALUE);
- System.out.println("------------------");
- System.out.println(Bean.STRING_VALUE);
- setFinalStatic(Bean.class.getField("STRING_VALUE"), "String_2");
- System.out.println(Bean.STRING_VALUE);
- System.out.println("------------------");
- System.out.println(Bean.BOOLEAN_VALUE);
- setFinalStatic(Bean.class.getField("BOOLEAN_VALUE"), true);
- System.out.println(Bean.BOOLEAN_VALUE);
- System.out.println("------------------");
- System.out.println(Bean.OBJECT_VALUE);
- setFinalStatic(Bean.class.getField("OBJECT_VALUE"), new Date());
- System.out.println(Bean.OBJECT_VALUE);
- }
- }
- class Bean {
- public static final int INT_VALUE = 100;
- public static final Boolean BOOLEAN_VALUE = false;
- public static final String STRING_VALUE = "String_1";
- public static final Object OBJECT_VALUE = "234";
- }
代码输出
100100------------------String_1String_1------------------falsetrue------------------234Fri Apr 25 00:55:05 CST 2014
说明
——其中的Boolean跟Object类型常量被正确修改了,而基本类型int和String的修改则“没有生效”。
0 0
- JAVA反射修改常量,以及其局限(修改private final限制)
- JAVA反射修改常量,以及其局限
- JAVA反射修改常量,以及其局限
- JAVA反射修改常量,以及其局限
- java 反射机制修改private final变量
- Java反射-修改String常量
- 通过反射修改 被 private final static修饰的成员
- java反射调用private方法,获取修改private属性值
- java反射修改private值和调用private函数
- Java 反射修改 final 属性值
- JavaSE 反射(进阶) 反射修改private成员
- 在Android中使用反射获取并修改private static final成员
- 使用反射修改final属性
- final Map可以修改内容,final 常量不能修改
- Java 反射有效的修改 final 属性值
- Java的private和final字段竟然可以被外部类修改?
- Java_反射_修改final static问题
- Java中String不可变性以及通过反射进行修改
- outlook HTML签名制作方法
- LA_3602_DNAConsensusString
- Hadoop/spark安装实战(系列篇1)准备安装包
- 静态查找表
- 开发一个安全的小网站(一)安全要素
- JAVA反射修改常量,以及其局限(修改private final限制)
- C++中#include <climits>
- MyEclipse从数据库反向生成实体类之Hibernate方式 反向工程
- HDOJ 题目3911 Black And White(线段树区间异或区间合并)
- 标签的语法
- SQL注入
- C++学习之模板
- Linux下java和javac版本不同(设置用户默认的java版本)解决方法
- 说说感想