使用反射修改final修饰的属性
先给个例子
public class FinalClass {
private final int intValue = 1; //编译前赋值的基础类型
private final Integer integerValue = new Integer(1); //基础类型的包装类
private final int noValueInt; //运行时赋值的基础类型
private final String literalString = "1"; //编译前赋值的LiteralString
private final String newString = new String("1"); //new关键字建的String对象
private final String noValueString; //运行时赋值的String对象
public int getIntValue() {
return intValue;
}
public Integer getIntegerValue() {
return integerValue;
}
public String getLiteralString() {
return literalString;
}
public String getNewString() {
return newString;
}
public int getNoValueInt() {
return noValueInt;
}
public String getNoValueString() {
return noValueString;
}
public FinalClass(int noValueInt, String noValueString) {
this.noValueInt = noValueInt;
this.noValueString = noValueString;
}
}
然后用反射试着修改一下属性值
public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception {
Field field = object.getClass().getDeclaredField(fieldName);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
if(!field.isAccessible()) {
field.setAccessible(true);
}
field.set(object, newFieldValue);
}
public static void main(String[] args) throws Exception {
FinalClass finalClass = new FinalClass(1, "1");
modify(finalClass, "intValue", 2);
modify(finalClass, "integerValue", 2);
modify(finalClass, "noValueInt", 2);
modify(finalClass, "literalString", "2");
modify(finalClass, "newString", "2");
modify(finalClass, "noValueString", "2");
System.out.println(finalClass.getIntValue()); //1
System.out.println(finalClass.getIntegerValue()); //2
System.out.println(finalClass.getNoValueInt()); //2
System.out.println(finalClass.getLiteralString()); //1
System.out.println(finalClass.getNewString()); //2
System.out.println(finalClass.getNoValueString()); //2
}
虽然代码顺利的执行了,并没有报错,但是有一些属性值似乎没有被修改,那么是不是真的没有被修改呢,我们用反射再看一下所有的属性值。
Field[] fields = finalClass.getClass().getDeclaredFields();
for (Field field : fields){
field.setAccessible(true);
System.out.println(field.get(finalClass)); //2
}
可以看到,所有的都被修改了,那么为什么有些属性我们调用get方法后得到的返回值仍然为1呢。我们看一下反编译后的代码。
public class FinalClass {
private final int intValue = 1;
private final Integer integerValue = new Integer(1);
private final int noValueInt;
private final String literalString = "1";
private final String newString = new String("1");
private final String noValueString;
public int getIntValue() {
return 1;
}
public Integer getIntegerValue() {
return this.integerValue;
}
public String getLiteralString() {
return "1";
}
public String getNewString() {
return this.newString;
}
public int getNoValueInt() {
return this.noValueInt;
}
public String getNoValueString() {
return this.noValueString;
}
public FinalClass(int var1, String var2) {
this.noValueInt = var1;
this.noValueString = var2;
}
}
这个时候就很明显了, Java 编译器对 final 属型进行了内联优化,即编译时把该 final 属性的值直接放到了引用它的地方。即使是反射修改了该属性,但是引用它的地方放的并不是属性引用而是属性值,所以返回值并没有变。
但是 Java 编译器不会对所有的 final 属型进行内联优化,只有八种基本类型属性和 LiteralString(直接用引号包裹而不是new关键字实例化的字符串) 会进行内联优化,对于引用类型不会则进行内联优化。此外,在运行期间才确定值的final属性也不会进行内联优化。