案发现场

经常听说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. Xcode磁盘空间清理

    http://www.iwangke.me/2013/09/09/clean-xcode-to-free-up-disk-space/#jtss-tsina 这个目录下面的文件也可以隔一段儿时间清理一 ...

  2. navigation ObtacleCostFunction源码分析

    ObtacleCostFunction 定义了一个ObstacleCostFunction类,继承自Trajectory类,Trajectory类有8个类参 总共有8个类参 double xv_,yv ...

  3. How to derive mean and variance of a Gaussian?

    PRML exercise 1.8: To derive mean: change of variable z = x - u, use symmetry To derive variance: di ...

  4. C#7.0新特性和语法糖详解

    转自IT之家网--DotNet码农:https://www.ithome.com/html/win10/305148.htm 伴随Visual Studio 2017的发布,C#7.0开始正式走上工作 ...

  5. Altium Designer chapter9总结

    改善系统的信号完整性和电磁兼容性需要注意如下: (1)系统电源尽量使用稳压输出. (2)高速期间器件与低俗器件隔离,避免低速器件影响高速器件. (3)模拟模块部分与数字模块部分分离. (4)为器件就近 ...

  6. promise 封装 axios

    /*axios({ method:"get", url:"./data.json", data:{ id:10 } }).then((res)=>{ co ...

  7. Lowest Common Ancestor of a Binary Tree(二叉树公共祖先)

    来源:https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree Given a binary tree, find t ...

  8. jJava第一周学习总结

    对于本学期要学习的Java程序语言,经过一周多的学习,我们首先在第一堂课了解了Java平台以及Java的历史,接着认识了JDK,初步了解了JDK的安装,JDK运行环境的设置,包括其中路径path的设置 ...

  9. MySQL-第十二篇管理结果集

    1.ResultSet 2.可更新的结果集,使用ResultSet的updateRow()方法.

  10. <每日一题> Day3:CodeForces-1141B.MaximalContinuousRest(简单题)

    题目链接 参考代码: #include <iostream> #include <algorithm> using namespace std; + ; int value[m ...