final 变量可以修改

在之前java 开发的认知中,final 修饰的变量一旦初始化,就不能被修改,如果是类变量,只能在构造方法中初始化,在其他方法中如果初始化,编译器也会报错,IDE也会拒绝编译。如下:

这个没问题,这是所有开发者的共识,但是如果遇到了反射,会有些不同,如下:

public 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 方法的源码如下

    public 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()的源码如下

  private 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,

    private 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源码如下:

 public FieldAccessor newFieldAccessor(Field var1, boolean var2) {
        checkInitted();
        return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2);
    }

跟中下去,因为方法比较长,不贴了,实际返回的是

UnsafeQualifiedObjectFieldAccessorImpl的对象,

这样返回到Field.set,实际就是调用UnsafeQualifiedObjectFieldAccessorImpl的set,源码如下:

   public 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

    public native void putObject(Object var1, long var2, Object var4);

是个native 方法,在Unsafe.cpp中实现,真正实现的是Unsafe_SetObjectVolatile方法

UNSAFE_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其实是修改了字段在内存中的值,所以编译器规则失效。

 

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值