JAVA反射改動常量,以及其局限

來源:互聯網
上載者:User

標籤:strong   nal   blank   size   object類   ssi   項目   rac   .com   

問題。以及一個解決方式

今天公司的JAVA項目碰到一個問題:在產生xls檔案的時候,假設資料較多。會出現ArrayIndexOutOfBoundsException。


Google發現是項中所用的jxl包(開源庫。用以處理xls檔案)的一個BUG。


也找到了一個解決的方法:http://www.blogjava.net/reeve/archive/2013/01/11/114564.html——即找到它的源碼。改動當中的一個靜態常量。然後又一次打包成jar就可以。試了一下,這種方法確實可行。

還有一個解決方式——反射

只是後來在公司前輩提醒,能夠試一下——

利用java的反射。在執行時將須要改動的常量強制更改成我們所須要的值

——這樣就不用改動jxl庫了。僅僅要在我們項目中加幾句就OK了,出問題的機率也會小非常多。
於是就研究了一下,儘管最後還是發如今這種方法在我們的項目不可行。只是還是非常有收穫的。

首先,利用反射改動私人靜態常量的方法對例如以下Bean類。當中的INT_VALUE是私人靜態常量
class Bean{    private static final Integer INT_VALUE = 100;}
改動常量的核心代碼:
    System.out.println(Bean.INT_VALUE);    //擷取Bean類的INT_VALUE欄位    Field field = Bean.class.getField("INT_VALUE");    //將欄位的訪問許可權設為true:即去除private修飾符的影響    field.setAccessible(true);    /*去除final修飾符的影響。將欄位設為可改動的*/    Field modifiersField = Field.class.getDeclaredField("modifiers");    modifiersField.setAccessible(true);    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);    //把欄位值設為200    field.set(null, 200);    System.out.println(Bean.INT_VALUE);

以上代碼輸出的結果是:

100
200

說明用反射私人靜態常量成功了。

方案的局限

注意到上述代碼的中的靜態常量類型是Integer——可是我們項目中實際須要改動的欄位類型並非封裝類型Integer,而是java的基本類型int。
當把常量的類型改成int之後,

class Bean{    private static final int INT_VALUE = 100;//把類型由Integer改成了int}

在其它代碼都不變的情況下。代碼輸出的結果居然變成了詭異的:

100
100

並且在調試的過程中發現。在第二次輸出的時候。記憶體中的Bean.INT_VALUE是已經變成了200,可是System.out.println(Bean.INT_VALUE)輸出的結果卻依舊時詭異的100?!

——反射失效了嗎?

又試了其它幾種類型,發現這樣的貌似失效的情會發生在int、long、boolean以及String這些基本類型上。而假設把類型改成Integer、Long、Boolean這樣的封裝類型,或者其它諸如Date、Object都不會出現失效的情況。

原因

經過一系列的研究、猜測、搜尋等過程。最終發現了原因:

對於基本類型的靜態常量,JAVA在編譯的時候就會把代碼中對此常量中引用的地方替換成對應常量值。

參考:Modifying final fields in Java
即對於常量 public static final int maxFormatRecordsIndex = 100 ,代碼

if( index > maxFormatRecordsIndex   ){    index  =  maxFormatRecordsIndex ;}     

這段代碼在編譯的時候已經被java自己主動最佳化成這種:

if( index > 100){    index = 100;}

所以在INT_VALUE是int類型的時候

System.out.println(Bean.INT_VALUE);//編譯時間會被最佳化成以下這樣:System.out.println(100);

所以,自然。不管怎麼改動Boolean.INT_VALUE,System.out.println(Bean.INT_VALUE)都還是會依舊固執地輸出100了。

——這本身是JVM的最佳化代碼提高執行效率的一個行為。可是就會導致我們在用反射改變此常量值時出現類似不生效的錯覺。

這大概是JAVA反射的一個局限吧——改動基本類型的常量時,不是太可靠。附一下我測試時候的DEMO吧代碼

import java.lang.reflect.Field;import java.lang.reflect.Modifier;import java.util.Date;public class ForClass {    static void setFinalStatic(Field field, Object newValue) throws Exception {        field.setAccessible(true);        Field modifiersField = Field.class.getDeclaredField("modifiers");        modifiersField.setAccessible(true);        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);        field.set(null, newValue);    }    public static void main(String args[]) throws Exception {        System.out.println(Bean.INT_VALUE);        setFinalStatic(Bean.class.getField("INT_VALUE"), 200);        System.out.println(Bean.INT_VALUE);        System.out.println("------------------");        System.out.println(Bean.STRING_VALUE);        setFinalStatic(Bean.class.getField("STRING_VALUE"), "String_2");        System.out.println(Bean.STRING_VALUE);                System.out.println("------------------");        System.out.println(Bean.BOOLEAN_VALUE);        setFinalStatic(Bean.class.getField("BOOLEAN_VALUE"), true);        System.out.println(Bean.BOOLEAN_VALUE);        System.out.println("------------------");        System.out.println(Bean.OBJECT_VALUE);        setFinalStatic(Bean.class.getField("OBJECT_VALUE"), new Date());        System.out.println(Bean.OBJECT_VALUE);    }}class Bean {    public static final int INT_VALUE = 100;    public static final Boolean BOOLEAN_VALUE = false;    public static final String STRING_VALUE = "String_1";    public static final Object OBJECT_VALUE = "234";}
代碼輸出
100100------------------String_1String_1------------------falsetrue------------------234Fri Apr 25 00:55:05 CST 2014

說明

——當中的Boolean跟Object類型常量被正確改動了,而基本類型int和String的改動則“沒有生效”。

同步發表在 http://www.barryzhang.com/archives/188

廣告一下我的新部落格。歡迎訪問哈~:BarryZhang.com  

JAVA反射改動常量,以及其局限

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.