1. 程式人生 > >JAVA反射修改常量,以及其侷限(修改private final限制)

JAVA反射修改常量,以及其侷限(修改private final限制)

轉載:PS:不過很好奇下面的那個private屬性怎麼被另一個類訪問到的。
注:又研究了下,發現特麼應該下面的修改常量的核心程式碼應該是在這個類的main函式裡面,坑,寫清楚點撒
對如下Bean類,其中的INT_VALUE是私有靜態常量
[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. class Bean{  
  2.     privatestaticfinal Integer INT_VALUE = 100;  
  3. }  
修改常量的核心程式碼:
[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. System.out.println(Bean.INT_VALUE);  
  2. //獲取Bean類的INT_VALUE欄位
  3. Field field = Bean.class.getField("INT_VALUE");  
  4. //將欄位的訪問許可權設為true:即去除private修飾符的影響
  5. field.setAccessible(true);  
  6. /*去除final修飾符的影響,將欄位設為可修改的*/
  7. Field modifiersField = Field.class.getDeclaredField("modifiers");  
  8. modifiersField.setAccessible(true);  
  9. modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
  10. //把欄位值設為200
  11. field.set(null200);  
  12. System.out.println(Bean.INT_VALUE);  

以上程式碼輸出的結果是:

100
200

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

方案的侷限

注意到上述程式碼的中的靜態常量型別是Integer——但是我們專案中實際需要修改的欄位型別並不是包裝型別Integer,而是java的基本型別int。
當把常量的型別改成int之後,

[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. class Bean{  
  2.     privatestaticfinalint INT_VALUE = 100;//把型別由Integer改成了int
  3. }  

在其他程式碼都不變的情況下,程式碼輸出的結果竟然變成了詭異的:

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 ,程式碼

[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. if( index > maxFormatRecordsIndex   ){  
  2.     index  =  maxFormatRecordsIndex ;  
  3. }       

這段程式碼在編譯的時候已經被java自動優化成這樣的:

[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. if( index > 100){  
  2.     index = 100;  
  3. }  

所以在INT_VALUE是int型別的時候

[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. System.out.println(Bean.INT_VALUE);  
  2. //編譯時會被優化成下面這樣:
  3. System.out.println(100);  

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

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

這大概是JAVA反射的一個侷限吧——修改基本型別的常量時,不是太可靠。

附一下我測試時候的DEMO吧

程式碼

[java] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. import java.lang.reflect.Field;  
  2. import java.lang.reflect.Modifier;  
  3. import java.util.Date;  
  4. publicclass ForClass {  
  5.     staticvoid setFinalStatic(Field field, Object newValue) throws Exception {  
  6.         field.setAccessible(true);  
  7.         Field modifiersField = Field.class.getDeclaredField("modifiers");  
  8.         modifiersField.setAccessible(true);  
  9.         modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
  10.         field.set(null, newValue);  
  11.     }  
  12.     publicstaticvoid main(String args[]) throws Exception {  
  13.         System.out.println(Bean.INT_VALUE);  
  14.         setFinalStatic(Bean.class.getField("INT_VALUE"), 200);  
  15.         System.out.println(Bean.INT_VALUE);  
  16.         System.out.println("------------------");  
  17.         System.out.println(Bean.STRING_VALUE);  
  18.         setFinalStatic(Bean.class.getField("STRING_VALUE"), "String_2");  
  19.         System.out.println(Bean.STRING_VALUE);  
  20.         System.out.println("------------------");  
  21.         System.out.println(Bean.BOOLEAN_VALUE);  
  22.         setFinalStatic(Bean.class.getField("BOOLEAN_VALUE"), true);  
  23.         System.out.println(Bean.BOOLEAN_VALUE);  
  24.         System.out.println("------------------");  
  25.         System.out.println(Bean.OBJECT_VALUE);  
  26.         setFinalStatic(Bean.class.getField("OBJECT_VALUE"), new Date());  
  27.         System.out.println(Bean.OBJECT_VALUE);  
  28.     }  
  29. }  
  30. class Bean {  
  31.     publicstaticfinalint INT_VALUE = 100;  
  32.     publicstaticfinal Boolean BOOLEAN_VALUE = false;  
  33.     publicstaticfinal String STRING_VALUE = "String_1";  
  34.     publicstaticfinal Object OBJECT_VALUE = "234";  
  35. }  

程式碼輸出

100
100
------------------
String_1
String_1
------------------
false
true
------------------
234
Fri Apr 25 00:55:05 CST 2014

說明

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