案发现场

经常听说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修饰的字段是否可以通过反射设置值的更多相关文章

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

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

  2. final修饰符

    final本身的含义是"最终的,不可变的",它可以修饰非抽象类,非抽象方法和变量.注意:构造方法不能使用final修饰,因为构造方法不能被继承,肯定是最终的. final修饰的类: ...

  3. final修饰符:

    知识点: 1.final关键字用于修饰类.变量和方法 2.有点类似C#里的 sealed 关键字,用于表示它修饰的方法.变量和类不可以再被改变 3.final修饰变量时,表示该变量一旦获取了初始值,就 ...

  4. Java中final修饰符深入研究

    一.开篇 本博客来自:http://www.cnblogs.com/yuananyun/ final修饰符是Java中比较简单常用的修饰符,同时也是一个被"误解"较多的修饰符.对很 ...

  5. java中final修饰符的使用

    1.final修饰符的用法: final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值. final可以修饰方法,被final修饰的方法不能被重写. final可以修饰类,被fi ...

  6. final修饰符(2)

    final局部变量 系统不会对局部变量进行初始化,局部变量必须又程序员显示初始化,因此使用final修饰局部变量,可以在声明时指定默认值,也可以在后面的代码中对该final变量赋初始值,但只能赋值一次 ...

  7. Java反射机制可以动态修改实例中final修饰的成员变量吗?

    问题:Java反射机制可以动态修改实例中final修饰的成员变量吗? 回答是分两种情况的. 1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了. ...

  8. 内部类访问局部变量为什么必须要用final修饰

    内部类访问局部变量为什么必须要用final修饰 看了大概五六篇博客, 讲的内容都差不多, 讲的内容也都很对, 但我觉得有些跑题了 略叙一下 String s = "hello"; ...

  9. JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配

    Java栈与堆 堆:顺序随意 栈:后进先出(Last-in/First-Out). Java的堆是一个运行时数据区,类的对象从中分配空间.这些对象通过new.newarray.anewarray和mu ...

随机推荐

  1. mysqladmin processlist; show processlist/status/open tables/engine/variables/table status/columns/grants/index/privileges/innodb status/logs/warnings/////; 结果筛选

    mysqladmin showprocesslist可查看完整sql需要权限. SHOW PROCESSLIST显示哪些线程正在运行.您也可以使用mysqladmin processlist语句得到此 ...

  2. sqlite时间类型

    SQLite分页显示:Select * From news order by id desc Limit 10 Offset 10这篇文章是根据 SQLite 官方 WIKI 里的内容翻译,如果有什么 ...

  3. python 多进程读写文件

    import time from multiprocessing import Process, JoinableQueue, cpu_count import csv ####处理一条数据的方法 d ...

  4. Delphi XE2 之 FireMonkey 入门(11) - 控件居中、旋转、透明

    RotationAngle.RotationCenter.Opacity 属性继承自 TControl(FMX.Types), 这些新属性成了控件的基本功能. 先在 HD 窗体上添加 TRectang ...

  5. js变量和数据类型

  6. 关于服务器无法在已发送http表头之后设置状态问题

    Response.ClearHeaders()方法 ClearHeaders方法只删除头信息,而不删除Response显示输出信息. this.Response.BufferOutput = true ...

  7. js json中的时间转换格式

    //根据json中的日期格式,转换成yyyy-mm-dd HH:mm:ss function ChangeDateFormat(cellval) { var date = new Date(parse ...

  8. vuer-cli 安装笔记

    电脑上装 的软件全卸载了.需要 重装 .整理了一下vue-cli脚手架搭建 1 先下载git 2 再下载node 3安装淘宝镜像 (https://npm.taobao.org/) 4 安装webpa ...

  9. java 历年版本特征(简化)

     无论是学习任何新知识,我都是从历史的角度着手,这让我对这些新知识是如何孕育出来的,有一个很好的理解 只从java 5-8,之后我也开始转nodejs了,感谢java伴随的这几年 Java5的新特性 ...

  10. MYSQL的ACID

    原子性 (Atomicity) 原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做. 隔离性 (Isolation) 隔离性是指多个事务并发执行的时候,事务内部的操作与其他事务 ...