当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数据域的更多相关文章

  1. .NET 利用反射将对象数据添加到数据库

    .NET 利用反射将对象数据添加到数据库   一些小型的项目,在不使用其他的框架(LINQ,NHibernate,EF等等框架)的前提下,这时候一些反复的增删改查就会让我们感到极其的繁琐,厌烦,为了避 ...

  2. winform中利用反射实现泛型数据访问对象基类(3)

    继续完善了几点代码 满足没有主键的情况下使用 并且完善实体字段反射设置value时的类型转换 /// <summary> /// DAO基类 实体名必须要与数据表字段名一致 /// < ...

  3. 使用反射修改final属性

    情型1:static final属性,无法修改其值. package m5.d7; import java.lang.reflect.Field; public class FieldTest { p ...

  4. winform中利用反射实现泛型数据访问对象基类(2)

    在1的基础上做了一点改进 参数化处理 看上去更简洁 无主键情况下 update 方法需要改进 insert delete没有问题  /// <summary>     /// DAO基类 ...

  5. winform中利用反射实现泛型数据访问对象基类(1)

    考虑到软件使用在客户端,同时想简化代码的实现,就写了一个泛型的数据访问对象基类,并不是特别健全,按道理应该参数化的方式实现insert和update,暂未使用参数化,抽时间改进. /// <su ...

  6. java反射修改final变量

    private void updateFinalModifiers(Field field) throws NoSuchFieldException, IllegalAccessException { ...

  7. jav利用反射修改类的静态变量

    有Student这个类: public class Student { private static String schoolName=""; private static St ...

  8. Java反射-修改字段值, 反射修改static final修饰的字段

    反射修改字段 咱们从最简单的例子到难, 一步一步深入. 使用反射修改一个private修饰符的变量name 咱们回到主题, 先用反射来实现一个最基础的功能吧. 其中待获取的name如下: public ...

  9. Java——利用反射机制将表单数据自动填充到JavaBean中

    以一个案例介绍反射机制的一种常见的使用场景,以及具体实现. 1.本文案例 在编写Java Web应用程序时,使用表单提交数据是一个必不可少的环节,后台对于前台使用表单提交的数据需要能够从请求中解析,并 ...

随机推荐

  1. 微信小程序清除默认样式

    1.清除button的默认样式 button::after{border:none;}input{outline:none;border:none;list-style: none;}

  2. 谈一谈你对js线程的理解

    js线程:javascript是单线程的,所有任务都需要排队,这些任务分为同步任务和异步任务,单线程上有一个主线程任务.同步任务必须再主线程上排队进行,而异步任务(类似于点击事件)必须在主线程上的任务 ...

  3. 《Redis设计与实现》- AOF持久化

    1. AOF持久化 Redis AOF 持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的. 2. RDB持久化与AOF持久化的区别 RDB持久化 RDB持久化通过保存数据中的键值对来记 ...

  4. 【笔记】objdump命令的使用

    ---恢复内容开始--- objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: objdump -f test 显示t ...

  5. Android 程序中获取一个反向 Shell

    代码如下: String command = "ls -al /"; Process process = Runtime.getRuntime().exec(command); 之 ...

  6. 20145202课后题,2.56&9.16

    我做的是2.56题,要求我用多组值来测试show_bytes 9.16 主要是对局部性进行了一些分析. 实验楼里面是空的,我电脑上显示不出来,所以我就做了一些书上的习题. 第十章的所有题目都被学长做过 ...

  7. 关于C、内存、栈的一些杂谈

    c的程序要手动管理内存的,所有的数据(结构)都可以分为两种存储方式,连续存储,顾名思义申请一片连续的内存以供使用(数组.结构体.共用体.栈.队列):非连续存储,顾名思义断断续续的的存储,那有一点这有一 ...

  8. [bzoj1552][Cerc2007]robotic sort&&[bzoj3506][Cqoi2014]排序机械臂

    非常垃圾的一道平衡树,结果被日了一天.很难受嗷嗷嗷 首先不得不说网上的题解让我这个本来就不熟悉平衡树的彩笔很难受——并不好理解. 还好Sinogi大佬非常的神,一眼就切掉了,而且用更加美妙的解法. 题 ...

  9. android stadio open recent 在同一窗口打开

    Android staido 有一个功能是open recent ,默认是下面这样的: 就出来一个框,给你选择,是在新的窗口打开,还是在当前窗口打开.如果你选了当前窗口,并且点了Remember,do ...

  10. 关于Python、Java、C#语言的一些比较

    不能说某某语言不好! 首先,千万别说某一个语言好不好,应为这样的用词是错的,我曾经在好多场合听到一些程序员说java好,.net不好这类的话. 其实语言不分好坏,只是在具体的某些领域或业务场景上不合适 ...