在之前java 开发的认知中,final 修饰的变量一旦初始化,就不能被修改,如果是类变量,只能在构造方法中初始化,在其他方法中如果初始化,编译器也会报错,IDE也会拒绝编译。如下:
这个没问题,这是所有开发者的共识,但是如果遇到了反射,会有些不同,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38public class OneCity { private final ArrayList<String> names; public OneCity() { names = new ArrayList<>(); names.add("hello"); } public String getValue() { return names.toString(); } } public class TestString { public static void main(String[] args) { OneCity oneCity = new OneCity(); System.out.println("反射前:" + oneCity.getValue()); try { Field nameField; nameField = OneCity.class.getDeclaredField("names"); nameField.setAccessible(true); // 这个同样不能少,除非上面把 private 也拿掉了,可能还得 public ArrayList<String> other = new ArrayList<>(); other.add("world"); nameField.set(oneCity,other ); System.out.println("反射后:" + oneCity.getValue()); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
输出的是:
反射前:[hello]
反射后:[world]
显然oneCity的names被替换了,这让我一脸懵逼,之前的认知瞬间被推翻,反射的威力实在太强大了,反射完美绕开了编译器的限制,那究竟背后藏了什么玄机,反射的威力这么强大呢,值得一探究竟。跟踪源码需要sun.reflect包,这个jdk源码没有包含,不过不要紧,sun.reflect包存在于jre/lib/rt.jar,反编译就可以,如果有源码的无所谓了。Field.set 方法的源码如下
1
2
3
4
5
6
7
8
9
10
11public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } getFieldAccessor(obj).set(obj, value); }
可以看到调用了getFieldAccessor(obj)返回的FieldAccessor的set方法,这个FieldAccessor是个接口。getFieldAccessor()的源码如下
1
2
3
4
5
6
7private FieldAccessor getFieldAccessor(Object obj) throws IllegalAccessException { boolean ov = override; FieldAccessor a = (ov) ? overrideFieldAccessor : fieldAccessor; return (a != null) ? a : acquireFieldAccessor(ov); }
初始时,overrideFieldAccessor和fieldAccessor都为null,变量a肯定null,接着调用acquireFieldAccessor,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18private FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck) { // First check to see if one has been created yet, and take it // if so FieldAccessor tmp = null; if (root != null) tmp = root.getFieldAccessor(overrideFinalCheck); if (tmp != null) { if (overrideFinalCheck) overrideFieldAccessor = tmp; else fieldAccessor = tmp; } else { // Otherwise fabricate one and propagate it up to the root tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck); setFieldAccessor(tmp, overrideFinalCheck); } return tmp; }
核心代码就是 tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck);reflectionFactory的类型是ReflectionFactory,我们查看,这时候反编译ReflectionFactory.class。reflectionFactory.newFieldAccessor源码如下:
1
2
3
4
5public FieldAccessor newFieldAccessor(Field var1, boolean var2) { checkInitted(); return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2); }
跟中下去,因为方法比较长,不贴了,实际返回的是
1
2UnsafeQualifiedObjectFieldAccessorImpl的对象,
这样返回到Field.set,实际就是调用UnsafeQualifiedObjectFieldAccessorImpl的set,源码如下:
1
2
3
4
5
6
7
8
9
10
11
12public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException { this.ensureObj(var1); if (this.isReadOnly) { this.throwFinalFieldIllegalAccessException(var2); } if (var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) { this.throwSetIllegalArgumentException(var2); } unsafe.putObjectVolatile(var1, this.fieldOffset, var2); }
关键代码是unsafe.putObjectVolatile,unsafe就是大名鼎鼎的Unsafe的对象,熟悉Unsafe的都知道,它可以直接访问系统内存资源,putObjectVolatile,这个方法名很奇怪,感觉和volatile有关系,先不管,这篇分析两者关系,https://blog.csdn.net/hanshengjian/article/details/86612767
1public native void putObject(Object var1, long var2, Object var4);
是个native 方法,在Unsafe.cpp中实现,真正实现的是Unsafe_SetObjectVolatile方法
1
2
3
4
5
6
7
8
9
10
11
12
13UNSAFE_ENTRY(void, Unsafe_SetObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) UnsafeWrapper("Unsafe_SetObjectVolatile"); oop x = JNIHandles::resolve(x_h); oop p = JNIHandles::resolve(obj); void* addr = index_oop_from_field_offset_long(p, offset); OrderAccess::release(); if (UseCompressedOops) { oop_store((narrowOop*)addr, x); } else { oop_store((oop*)addr, x); } OrderAccess::fence(); UNSAFE_END
核心就是调用了oop_store方法,从方法意思就是替换偏移量offset的字段指向更新为传入的x_h对象,这个x_h就是Field.set传入的第二个参数。
总结一下:Field.set其实是修改了字段在内存中的值,所以编译器规则失效。
最后
以上就是自由鸡最近收集整理的关于final 变量可以修改的全部内容,更多相关final内容请搜索靠谱客的其他文章。
发表评论 取消回复