假设我们有如下一个类,我们要利用反射来对其成员变量就行修改:
class Entity {
public int i = 1;
}
一般我们会这么做:
try {
Entity e = new Entity();
System.out.println("before: " + e.i);
Field f = Entity.class.getDeclaredField("i");
f.setInt(e, 2);
// 或者这样
//f.set(e, 2);// java会自动装箱拆箱
System.out.println("after: " + e.i);
} catch (Exception e) {
e.printStackTrace();
}
结果输出:
before: 1
after: 2
但是如果我们的成员变量是final呢?
class Entity {
public final int i = 1;
}
我们再用上面的方式来进行修改值,发现出异常了:
java.lang.IllegalAccessException: Can not set final int field com.test.Entity.i to (int)2
那我们该怎么办呢?我们可以修改成员变量的final修饰符,使其变为public int i = 1;具体方法如下:
try {
Entity e = new Entity();
System.out.println("before: " + e.i);
Field f = Entity.class.getDeclaredField("i");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
// 输出17:表示修饰符为:public final
System.out.println(f.getModifiers());
/* 这里就是要修改修饰符了,至于为什么是f.getModifiers() & ~Modifier.FINAL,大家看一下Modifier的源码就知道了*/
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
// 输出1:表示修饰符已经被修改为:public
System.out.println(f.getModifiers());
f.setAccessible(true);
f.setInt(e, 2);
//f.set(e, 3);
System.out.println("after: " + e.i);
} catch (Exception e) {
e.printStackTrace();
}
没有抛出异常了,但是确没有出现我们想要的结果,输出为:
before: 1
17
1
after: 1
这是为什么呢?我们稍微修改下Entity的代码如下:
class Entity {
public final Integer i = 1;
}
然后再执行,发现结果已经被修改了:
before: 1
17
1
after: 3
说明我可以对对象数据类型进行修改,而不能对基本数据类型进行修改
我们再来试试String类型的:
class Entity {
public final String s = "a";
}
try {
Entity e = new Entity();
System.out.println("before: " + e.s);
Field f = Entity.class.getDeclaredField("s");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
System.out.println(f.getModifiers());
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
System.out.println(f.getModifiers());
f.setAccessible(true);
//f.setInt(e, 2);
f.set(e, "b");
System.out.println("after: " + e.s);
} catch (Exception e) {
e.printStackTrace();
}
结果输出:
before: a
17
1
after: a
咦?竟然木有改变?
我们再对Entity稍微改动下:
class Entity {
public final String s = new String("a");
}
结果输出:
before: a
17
1
after: b
修改成功,至于为什么会这样,那得问java虚拟机了
好了,总结一下,对于final修改的成员变量,基本数据以及public final String s = "a";这种方式不可被修改
而对于对象数据类型是可以突破final限制进行修改的,但是一般我们也很少会这么用