案发现场
经常听说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修饰的字段被初始化了,引用的地址就不能发生变化,但是堆中的对象是可以被修改的。而字符串常量是不能被修改的,所以出现了,反射和实例去取出的数据不一致情况。