之前在阅读其他源码的时候,想要修改其中被 final 修饰符修饰的字段的值,可行吗?
解决方案
使用Java反射,通过 Field#setAccessible(true)
将 private 修饰的字段变为 accessible;再将 final 修饰符去掉;最后再设置新值即可。当然,如果涉及到 Java 内联优化,则会失效。具体见示例代码:
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 38 39 40
| package com.tianma.sample;
import java.lang.reflect.Field; import java.lang.reflect.Modifier;
public class ChangeStaticFinalFieldSample {
static void changeStaticFinal(Field field, Object newValue) throws Exception { field.setAccessible(true);
Field modifersField = Field.class.getDeclaredField("modifiers"); modifersField.setAccessible(true); modifersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue); }
public static void main(String[] args) throws Exception { Sample.print(); Field canChangeField = Sample.class.getDeclaredField("CAN_CHANGE"); Field cannotChangeField = Sample.class.getDeclaredField("CANNOT_CHANGE"); changeStaticFinal(canChangeField, 2); changeStaticFinal(cannotChangeField, 3); Sample.print(); } }
class Sample { private static final int CAN_CHANGE = new Integer(1); private static final int CANNOT_CHANGE = 1;
public static void print() { System.out.println("CAN_CHANGE = " + CAN_CHANGE); System.out.println("CANNOT_CHANGE = " + CANNOT_CHANGE); System.out.println("------------------------"); } }
|
打印结果为:
1 2 3 4 5 6
| CAN_CHANGE = 1 CANNOT_CHANGE = 1 ------------------------ CAN_CHANGE = 2 CANNOT_CHANGE = 1 ------------------------
|
通过以上输出结果可以看出, CAN_CHANGE
和 CANNOT_CHANGE
字段同属于 final 修饰符修饰的常量字段,但是由于 CANNOT_CHANGE
常量在 Java 编译过程中使用了内联优化,其值在编译阶段就被编译为常量值 1
,故而使用内联优化的 final 字段更改其值是无效的; 而 CAN_CHANGE
字段未被内联优化,故而能通过 Java 反射对其值进行修改。
参考
Change private static final field using Java reflection