利用反射對修飾符爲final的成員變量進行修改

假設咱們有以下一個類,咱們要利用反射來對其成員變量就行修改:java

class Entity {
	public int i = 1;
}

通常咱們會這麼作:spa

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();
}

結果輸出:code

before: 1
after: 2

可是若是咱們的成員變量是final呢?
對象

class Entity {
	public final int i = 1;
}

咱們再用上面的方式來進行修改值,發現出異常了:get

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的代碼以下:it

class Entity {
	public final Integer i = 1;
}

而後再執行,發現結果已經被修改了:
io

before: 1
17
1
after: 3

說明我能夠對對象數據類型進行修改,而不能對基本數據類型進行修改class

咱們再來試試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限制進行修改的,可是通常咱們也不多會這麼用

相關文章
相關標籤/搜索