Final修饰的字段是否可以通过反射设置值
案发现场
经常听说final修饰的字段是常量不能改变的他的值,但是以外发现 Integer.java源码中的字段“value”是final,但是可以通过反射改变他的值。
public final class Integer extends Number implements Comparable<Integer> {
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
public Integer(int value) {
this.value = value;
}
}
验证final修饰的字段是否可以修改
对final修饰的成员非静态变量做强制修改
package test; import java.lang.reflect.Field; /**
* @author kancy
* @version 1.0
* @date 2019/3/7 12:28
*/
public class FinalDemo {
/**常量:默认值null*/
private final String v1 = null;
/**常量:默认值v4*/
private final String v2 = "v2"; public static void main(String[] args) throws Exception {
FinalDemo finalDemo = new FinalDemo();
Field f1 = finalDemo.getClass().getDeclaredField("v1");
Field f2 = finalDemo.getClass().getDeclaredField("v2");
f1.setAccessible(true);
f2.setAccessible(true); // 反射改变v1的值
f1.set(finalDemo, "new_v1");
System.out.println("v1 改变后的值(对象取值):" + finalDemo.getV1());
System.out.println("v1 改变后的值(反射取值):" + f1.get(finalDemo));
// 反射改变v2的值
f2.set(finalDemo, "new_v2");
System.out.println("v2 改变后的值(对象取值):" + finalDemo.getV2());
System.out.println("v2 改变后的值(反射取值):" + f2.get(finalDemo));
System.out.println("--------------------------------------------------------");
// 反射改变v1的值
f1.set(finalDemo, "new_new_v1");
System.out.println("v1 再次改变后的值(对象取值):" + finalDemo.getV1());
System.out.println("v1 再次改变后的值(反射取值):" + f1.get(finalDemo));
// 反射改变v2的值
f2.set(finalDemo, "new_new_v2");
System.out.println("v2 再次改变后的值(对象取值):" + finalDemo.getV2());
System.out.println("v2 再次改变后的值(反射取值):" + f2.get(finalDemo));
} public String getV1() {
return v1;
} public String getV2() {
return v2;
}
}
结果打印:
v1 改变后的值(对象取值):new_v1
v1 改变后的值(反射取值):new_v1
v2 改变后的值(对象取值):v2
v2 改变后的值(反射取值):new_v2
--------------------------------------------------------
v1 再次改变后的值(对象取值):new_new_v1
v1 再次改变后的值(反射取值):new_new_v1
v2 再次改变后的值(对象取值):v2
v2 再次改变后的值(反射取值):new_new_v2
结论:对于非静态的final成员变量,在没有赋值的情况下是可以使用反射对其进行赋值的;对于已经初始化赋值的变量,反射不能真正该变变量的值,但是使用反射get是可以获取到改变后的值,用实例是无法获取到的。
对final修饰的成员非静态变量做强制修改
import java.lang.reflect.Field; /**
* @author kancy
* @version 1.0
* @date 2019/3/7 12:35
*/
public class StaticFinalDemo {
/**
* 静态常量 默认值null
*/
private static final String v1 = null;
/**
* 静态常量 默认值v3
*/
private static final String v2 = "v2"; public static void main(String[] args) throws Exception {
Field f1 = StaticFinalDemo.class.getDeclaredField("v1");
Field f2 = StaticFinalDemo.class.getDeclaredField("v2");
f1.setAccessible(true);
f2.setAccessible(true); // 反射改变v1的值
try {
f1.set(StaticFinalDemo.class, "new_v1");
System.out.println("v1 改变后的值(对象取值):" + StaticFinalDemo.getV2());
System.out.println("v1 改变后的值(反射取值):" + f1.get(StaticFinalDemo.class));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 反射改变v2的值
try {
f2.set(StaticFinalDemo.class, "new_v2");
System.out.println("v2 改变后的值(对象取值):" + StaticFinalDemo.getV2());
System.out.println("v2 改变后的值(反射取值):" + f2.get(StaticFinalDemo.class));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} } public static String getV1() {
return v1;
} public static String getV2() {
return v2;
}
}
结果:
java.lang.IllegalAccessException: Can not set static final java.lang.String field StaticFinalDemo.v1 to java.lang.String
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:758)
at StaticFinalDemo.main(StaticFinalDemo.java:26)
java.lang.IllegalAccessException: Can not set static final java.lang.String field StaticFinalDemo.v2 to java.lang.String
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:758)
at StaticFinalDemo.main(StaticFinalDemo.java:36)
结论:对静态的final修饰的字段(赋值或者不赋值)进行修改,会报错:java.lang.IllegalAccessException错误。
反射的set的过程
https://www.edrawsoft.cn/viewer/public/s/13c34261013087
如果final修饰的类型为Object引用类型
package test; import java.lang.reflect.Field; /**
* @author kancy
* @version 1.0
* @date 2019/3/7 12:28
*/
public class FinalDemo {
/**常量:默认值null*/
private final String v1 = null;
/**常量:默认值v4*/
private final String v2 = "v2";
private final ChildA v3 = new ChildA("kancy");
private final ChildA v4 = null; public FinalDemo() {
} public static void main(String[] args) throws Exception {
FinalDemo finalDemo = new FinalDemo();
Field f3 = finalDemo.getClass().getDeclaredField("v3");
f3.setAccessible(true);
ChildA oldValue = finalDemo.getV3();
System.out.println("v3 改变前的值:" + finalDemo.getV3()+", " );
f3.set(finalDemo, new ChildA("pmm"));
System.out.println("v3 改变后的值(对象取值):" + finalDemo.getV3()+", ");
System.out.println("v3 改变后的值(反射取值):" + f3.get(finalDemo)+", ");
// 地址已经发生变化
System.out.println(oldValue == finalDemo.getV3()); } public ChildA getV3() {
return v3;
} public static class ChildA {
private String name; public ChildA(String name) {
this.name = name;
} public String getName() {
return name;
}
}
}
结果:
v3 改变前的值:test.FinalDemo$ChildA@66d3c617,
v3 改变后的值(对象取值):test.FinalDemo$ChildA@63947c6b,
v3 改变后的值(反射取值):test.FinalDemo$ChildA@63947c6b,
false 进程已结束,退出代码 0
结论:可以看出实例和反射取出的对象都是同一个,而使用字符类型就不是同一种类型,这是因为字符存在于常量池中,而对象存在与堆区。field.set是直接通过unsafe操作内存的,一但fianl修饰的字段被初始化了,引用的地址就不能发生变化,但是堆中的对象是可以被修改的。而字符串常量是不能被修改的,所以出现了,反射和实例去取出的数据不一致情况。
Final修饰的字段是否可以通过反射设置值的更多相关文章
- Java反射-修改字段值, 反射修改static final修饰的字段
反射修改字段 咱们从最简单的例子到难, 一步一步深入. 使用反射修改一个private修饰符的变量name 咱们回到主题, 先用反射来实现一个最基础的功能吧. 其中待获取的name如下: public ...
- final修饰符
final本身的含义是"最终的,不可变的",它可以修饰非抽象类,非抽象方法和变量.注意:构造方法不能使用final修饰,因为构造方法不能被继承,肯定是最终的. final修饰的类: ...
- final修饰符:
知识点: 1.final关键字用于修饰类.变量和方法 2.有点类似C#里的 sealed 关键字,用于表示它修饰的方法.变量和类不可以再被改变 3.final修饰变量时,表示该变量一旦获取了初始值,就 ...
- Java中final修饰符深入研究
一.开篇 本博客来自:http://www.cnblogs.com/yuananyun/ final修饰符是Java中比较简单常用的修饰符,同时也是一个被"误解"较多的修饰符.对很 ...
- java中final修饰符的使用
1.final修饰符的用法: final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值. final可以修饰方法,被final修饰的方法不能被重写. final可以修饰类,被fi ...
- final修饰符(2)
final局部变量 系统不会对局部变量进行初始化,局部变量必须又程序员显示初始化,因此使用final修饰局部变量,可以在声明时指定默认值,也可以在后面的代码中对该final变量赋初始值,但只能赋值一次 ...
- Java反射机制可以动态修改实例中final修饰的成员变量吗?
问题:Java反射机制可以动态修改实例中final修饰的成员变量吗? 回答是分两种情况的. 1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了. ...
- 内部类访问局部变量为什么必须要用final修饰
内部类访问局部变量为什么必须要用final修饰 看了大概五六篇博客, 讲的内容都差不多, 讲的内容也都很对, 但我觉得有些跑题了 略叙一下 String s = "hello"; ...
- JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配
Java栈与堆 堆:顺序随意 栈:后进先出(Last-in/First-Out). Java的堆是一个运行时数据区,类的对象从中分配空间.这些对象通过new.newarray.anewarray和mu ...
随机推荐
- [CSP-S模拟测试]:计数(DP+记忆化搜索)
题目描述 既然是萌萌哒$visit\text{_}world$的比赛,那必然会有一道计数题啦!考虑一个$N$个节点的二叉树,它的节点被标上了$1\sim N$的编号.并且,编号为$i$的节点在二叉树的 ...
- vue-cli目录结构介绍
一个vue-cli的项目结构如下: 文件结构细分: 1.build——[webpack配置] build文件主要是webpack的配置,主要启动文件是dev-server.js,当我们输入npm ru ...
- HTML5和CSS3兼容清单
1.CSS3 2.CSS3选择器 3.HTML5 4.HTML5 From
- 测试markdorn
专业主义 描述:这本书着重阐释了真正的专家必须具备的四种能力:**先见能力**.构思能力.讨论的能力.适应矛盾的能力,以丰富的案例和深刻的洞见警示人们重新思考专业的内涵与效用,培养并吸纳专业人才. 状 ...
- MySQL与MongoDB的不同
数据存放的巨大不同
- scrapy Pipeline使用twisted异步实现mysql数据插入
from twisted.enterprise import adbapi class MySQLAsyncPipeline: def open_spider(self, spider): db = ...
- debian 配置静态ip
1. 查看虚拟机上本机ipcmd→ipconfig 2. 配置网卡2.1 备份原有配置文件配置文件cp /etc/network/interfaces /etc/network/interfacesb ...
- Hadoop实战内容摘记
Hadoop 开源分布式计算平台,前身是:Apache Nutch(爬虫),Lucene(中文搜索引擎)子项目之一. 以Hadoop分布式计算文件系统(Hadoop Distributed File ...
- JsonDatetime
ToDatetime public DateTime JsonDateTimeConvert(string time) { //try //{ if (String.IsNullOrEmpty(tim ...
- package__init__用途
baidu包,假设在baidu包下有N个模块,分别是baidu1.py.baidu2.py,baidu3.py, baiduHq.py(baidu1.py,baidu2.py,baidu3.py模块代 ...