利用反射修改final数据域
当final修饰一个数据域时,意义是声明该数据域是最终的,不可修改的。常见的使用场景就是eclipse自动生成的serialVersionUID一般都是final的。
另外还可以构造线程安全(thread safe)的immutable类,比如String,其数据域都是final的。这些使用场景都建立在final不可修改这个条件上,但是,反射可以打破这一切。
1.利用反射修改final数据域
首先,构造一个Person类,里面有个final字段NAME。我们尝试着修改这个字段。顺利的出乎意料。
public class Person {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Person p = new Person();
Field field = p.getClass().getDeclaredField("NAME");
field.setAccessible(true);
field.set(p,"Hello");
System.out.println(field.get(p));
//p.printName();
}
private final String NAME = "Clive";
public Person() {
}
public void printName() {
System.out.println(NAME);
}
}
/***************
console print:
Hello
***************/
2.内联与内联消除
NAME数据域如此简单的就被修改了,final真是太"不安全了"! 但是,当我们调用p.printName() 时,控制台打印的却是"Clive"字符串。这是因为JVM做了优化处理, 当一个数据域被final修饰,那就表明这个数据域是常量,JVM会把所有NAME数据域出现的地方全部用"Clive"替换掉, 比如 printName() 方法其实被优化成了这样。
public void printName() { System.out.println("Clive"); }
所以,要想不被自动优化,就要把代码弄得复杂点,如下
public class Person {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Person p = new Person();
Field field = p.getClass().getDeclaredField("NAME");
field.setAccessible(true);
field.set(p,"Hello");
System.out.println(field.get(p));
p.printName();
}
private final String NAME =(null!=null?"Clive":"Clive"); //声明时即初始化
public Person() {
//或者,在这里设置NAME数据域的值
//NAME="Clive";
}
public void printName() {
System.out.println(NAME);
}
}
/***************
console print:
Hello
Hello
***************/
结果见 console print,顺利消除了优化,final字段最终被修改了!
3.修改static final数据域
如果在NAME字段再增加一个static关键字修饰,然后再用反射修改的话就不行了, 会抛出异常
java.lang.IllegalAccessException: Can not set static final int field ...
这时,修改Field中的modifiers数据域,清除代表final的那个bit,才可以成功修改。
public class Person {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Person p = new Person();
Field field = p.getClass().getDeclaredField("NAME");
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);//fianl标志位置0
field.set(p,"Hello");
System.out.println(field.get(p));
p.printName();
}
private final String NAME =(null!=null?"Clive":"Clive");
public Person() {
}
public void printName() {
System.out.println(NAME);
}
}
/**************
console print:
Hello
Hello
**************/
总结
这个知识点感觉知道就好,平时还是不要修改final数据域的好 :)
引用
1.https://www.oschina.net/question/1245392_159103
2.https://github.com/jOOQ/jOOR
利用反射修改final数据域的更多相关文章
- .NET 利用反射将对象数据添加到数据库
.NET 利用反射将对象数据添加到数据库 一些小型的项目,在不使用其他的框架(LINQ,NHibernate,EF等等框架)的前提下,这时候一些反复的增删改查就会让我们感到极其的繁琐,厌烦,为了避 ...
- winform中利用反射实现泛型数据访问对象基类(3)
继续完善了几点代码 满足没有主键的情况下使用 并且完善实体字段反射设置value时的类型转换 /// <summary> /// DAO基类 实体名必须要与数据表字段名一致 /// < ...
- 使用反射修改final属性
情型1:static final属性,无法修改其值. package m5.d7; import java.lang.reflect.Field; public class FieldTest { p ...
- winform中利用反射实现泛型数据访问对象基类(2)
在1的基础上做了一点改进 参数化处理 看上去更简洁 无主键情况下 update 方法需要改进 insert delete没有问题 /// <summary> /// DAO基类 ...
- winform中利用反射实现泛型数据访问对象基类(1)
考虑到软件使用在客户端,同时想简化代码的实现,就写了一个泛型的数据访问对象基类,并不是特别健全,按道理应该参数化的方式实现insert和update,暂未使用参数化,抽时间改进. /// <su ...
- java反射修改final变量
private void updateFinalModifiers(Field field) throws NoSuchFieldException, IllegalAccessException { ...
- jav利用反射修改类的静态变量
有Student这个类: public class Student { private static String schoolName=""; private static St ...
- Java反射-修改字段值, 反射修改static final修饰的字段
反射修改字段 咱们从最简单的例子到难, 一步一步深入. 使用反射修改一个private修饰符的变量name 咱们回到主题, 先用反射来实现一个最基础的功能吧. 其中待获取的name如下: public ...
- Java——利用反射机制将表单数据自动填充到JavaBean中
以一个案例介绍反射机制的一种常见的使用场景,以及具体实现. 1.本文案例 在编写Java Web应用程序时,使用表单提交数据是一个必不可少的环节,后台对于前台使用表单提交的数据需要能够从请求中解析,并 ...
随机推荐
- iOS MapKit地图
地图框架:#import <MapKit/MapKit.h> 基本属性和方法: 属性: 地图类视图:MKMapView 地图类型:MKMapType mapType 地图旋转:rotate ...
- 常见的HTTP状态码有哪些?
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求.当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求. ...
- SAP库存历史库存表更新逻辑 (转)
根据库存类型的不同,库存信息保存在不同的表中,具体而言见下表 库存类型 当前库存 历史库存 库存金额 历史库存金额 工厂级别库存 MARC MARCH MBEW MBEWH MBEW 库存地点库存 M ...
- SpringMVC使用ModelAndView的相对路径和绝对路径的问题
例如:abc/a/a.jsp,想要跳转到根目录的b.jsp 使用 ModelAndView 跳转, 若引用:org.springframework.web.portlet.ModelAndView 这 ...
- python3 井字棋 GUI - 人机对战、机器对战 (threading、tkinter库)
python3 井字棋 GUI - 人机对战.机器对战 功能 GUI界面 人机对战(可选择机器先走) 机器对战(50局) 流程图 内核 棋盘 [0][1][2] [3][4][5] [6][7][8] ...
- nodejs的http-server--web前端福利
很多web前端在日常开发的时候可能会想常开发是谁. 不好意思,说错了. 很多web前端在日常开发的时候总是避免不了让所写页面在服务器环境下执行. 比如当你在用angularjs的route模块等等等. ...
- uniqueidentifier数据类型转换
cast(id as varchar(36))
- 【笔记】objdump命令的使用
---恢复内容开始--- objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: objdump -f test 显示t ...
- 操作视频-对视频进行canny边缘检测
#include<opencv2/opencv.hpp> using namespace cv; int main() { VideoCapture capture(); //从摄像头读入 ...
- 2 http协议
http协议简介: HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文 ...