假設我們有如下一個類,我們要利用反射來對其成員變量就行修改:
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限制進行修改的,但是一般我們也很少會這麼用