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 ...
随机推荐
- vue 实现,子组件向父组件 传递数据
首先理清组件之间的关系 组件与组件之间,还存在着不同的关系.父子关系与兄弟关系(不是父子的都暂称为兄弟吧). 父子组件 父子关系即是组件 A 在它的模板中使用了组件 B,那么组件 A 就是父组件,组件 ...
- Flask框架【七】—session组件详解
一.flask session简介 flask中session组件可分为内置的session组件还有第三方flask-session组件,内置的session组件缺点: 功能单一 session是保存 ...
- Week2 - 669. Trim a Binary Search Tree & 617. Merge Two Binary Trees
Week2 - 669. Trim a Binary Search Tree & 617. Merge Two Binary Trees 669.Trim a Binary Search Tr ...
- 类StringBuffer
1字符串声明和创建 public StringBuffer() 无参构造函数 public StringBuffer(int capacity) 指定容量的字符串缓冲区对象 public String ...
- Java ——日期时间 日期时间相关类 随机数 定义类属性时建议使用引用数据类型
本节重点思维导图 Date对象创建 两个构造函数: Date() ----使用当前日期和时间来初始化对象 Date(long millisec) -----接收一个参数,该参数是从1970年1月1日起 ...
- linux下安装python27 nginx 和uwsgi
注意: python27 默认没有安装 pip 和setuptools所以要提前安装.(务必先提前安装python27 哈 ) wget --no-check-certificate https:// ...
- python nginx+uwsgi+WSGI 处理请求详解
https://blog.csdn.net/a519640026/article/details/76157976 请求从 Nginx 到 uwsgi 到 django 交互概览 作为python w ...
- 安全运维 - Windows系统应急响应
挖矿病毒应急 传播方式: 通过社工.钓鱼方式下载和运行了挖矿程序(邮件.IM等) 利用计算机系统远程代码执行漏洞下载.上传和执行挖矿程序. 利用i算计Web或第三方软件漏洞获取计算机权限,然后下载和执 ...
- String类为什么是final
String 本身一个对象,对象在jvm启动的时候就要实例化和其他类调用就要实例化,第一性能,第二安全,因为string的高频繁的使用,如果被继承,哪里性能将会大大降低,因为不能被继承,换句话来说就比 ...
- Vue 基础 day03
定义Vue 组件 什么是组件:组件的出现,就是为了拆分 Vue 实例的代码量,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件: 组件化和模块化的不同: ...