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

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

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

結果輸出:

before1
after2

但是如果我們的成員變量是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();
}

沒有拋出異常了,但是確沒有出現我們想要的結果,輸出爲:

before1
17
1
after1

這是爲什麼呢?我們稍微修改下Entity的代碼如下:

class Entity {
    public final Integer i = 1;
}

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

before1
17
1
after3

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

我們再來試試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();
}

結果輸出:

beforea
17
1
aftera

咦?竟然木有改變?

我們再對Entity稍微改動下:

class Entity {
    public final String s = new String("a");
}

結果輸出:

beforea
17
1
after: b

修改成功,至於爲什麼會這樣,那得問java虛擬機了


好了,總結一下,對於final修改的成員變量,基本數據以及public final String s = "a";這種方式不可被修改

而對於對象數據類型是可以突破final限制進行修改的,但是一般我們也很少會這麼用

發佈了32 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章