案发现场

经常听说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. python条件判断之直接加数字

    if 后面跟的是条件表达式,条件表达式的结果为True或者False. (1)如果if后面的条件是数字,只要这个数字不是0,python都会把它当做True处理,见下面的例子: if 3: print ...

  2. git 指定从其他分支拉取commit

    git cherry-pick commit-id  (github 上的短号)

  3. skip a transaction in goldengate

    skip a transaction in goldengate [oracle@db ]$ ggsci Oracle GoldenGate Command Interpreter for Oracl ...

  4. 图论之点双&边双

    说人话: 边双联通: a到b的路径上无必经边 点双联通: a到b的路径上除了a,b没有必经点 tarjan求点双联通: 代码(补图) 割点: 桥: 求点双:强制dfs时不越过割点,即可求出一个块 求边 ...

  5. 图书-软件架构:《Design Patterns: Elements of Reusable Object-Oriented Software》(即后述《设计模式》一书)

    ylbtech-图书-软件架构:<Design Patterns: Elements of Reusable Object-Oriented Software>(即后述<设计模式&g ...

  6. VS2012下自定义打开文件对话框

    VS2012下自定义打开文件对话框,MFC的CFileDialog封装了太多,太复杂,绕得头晕,自己封装一个得了 #pragma once #include <objbase.h> #in ...

  7. 16/7/7_PHP-面向对象关键词

    转载地址: http://blog.sina.com.cn/s/blog_5182b171010092fb.html PHP5 是一具备了大部分面向对象语言的特性的语言,比PHP4 有了很多的面向对象 ...

  8. DP---DAG、背包、LIS、LCS

    DP是真的难啊,感觉始终不入门路,还是太弱了┭┮﹏┭┮ DAG上的DP ​ 一般而言,题目中如果存在明显的严格偏序关系,并且求依靠此关系的最大/最小值,那么考虑是求DAG上的最短路或者是最长路.(据说 ...

  9. 在 linux 中 find 和 grep 的区别??

    Linux 系统中 grep 命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.grep 全称是 Global Regular Expression Print,表示全局 ...

  10. SCUT - 142 - 第n个素数

    https://scut.online/p/142 但是洲阁筛打表还是超时了,打的表不够长吧,在51nod上面要跑5s.要是快10倍得要密1000倍,根本打不出来(时间意义). 暴力check要找的质 ...