在刚哥的知识星球中,看到有网友询问
反射给final修饰的字段设值,为啥设值会失败
,之前也深入学习一下反射,对这个问题有点迷惑,于是学习起来并写demo实践一下。
先创建一个Test类,里面含有final修饰的变量
1 | public class Test { |
然后通过反射修改
1 | public class Client { |
看上面的输出,修改后再反射获取修改的变量,是修改成功的,但是,调用方法获取,发现返回还是修改前的值,为啥呢?
这里要涉及到Java虚拟机,在Java类加载阶段的准备阶段,会对被final修饰的属性做优化。在编译期被优化后的Test.class如下:
1 | public class Test { |
所以,通过反射修改NAME的值,再调用该方法也修改无效。要注意一点,NAME是可以被反射修改且修改成功了。
上面说到,只有基本数据类型和String类型才会做优化导致修改无效。对于包装类或者对象类型,还是可以修改成功的,不信,看下面的代码
1 | private final String JOB = new String("安卓程序员"); |
看到输出可以发现,final被赋值后,还是可以通过反射重新赋值的。包装类也是,如下代码
1 | private final Integer AGE = new Integer(18); |
继续看Test.class文件,看看被编译后的内容,没有被优化替换掉
1 | public class Test { |
另外,final类型定义后,不一定需要立马赋值,可以在构件函数进行初始化,那么,能否修改呢?said is null,直接上代码。
1 | public class Test { |
同样还是修改有效的,继续看Test.class文件,发现被优化后,构造函数的值,被直接移动到final的定义中,方法返回的是LOCATION的引用。
1 | public class Test { |
总结
回到一开始的问题,final属性值能否被修改呢?这个就要看final修饰变量的类型以及初始化的时机,通过看编译后的class文件就可以知道了。