JAVA反射修改常量,以及其局限
轉載自?JAVA反射修改常量,以及其局限
問題,以及一個解決方案
今天公司的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"; } 代碼輸出 100 100 ------------------ String_1 String_1 ------------------ false true ------------------ 234 Fri Apr 25 00:55:05 CST 2014說明
——其中的Boolean跟Object類型常量被正確修改了,而基本類型int和String的修改則“沒有生效”。
以上代碼輸出的結果是:總結
以上是生活随笔為你收集整理的JAVA反射修改常量,以及其局限的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 麻雀多少集 麻雀简单介绍
- 下一篇: 如何使手机充电更快速高效