java使用反射強制給private字段賦值


今天項目中遇到了一個問題,要調用一個類,並獲取這個類的屬性進行賦值然后將這個類傳遞到方法中做為參數。

實際操作時才發現,這個類中的字段屬性是私有的,不能進行賦值!沒有提供公有的方法。而這個類又是打包成jar給我的,我還不能更改它的代碼,以至於想手動給它寫個set方法都是問題。后來想到用反射可以解決這個問題,於是試了一下,果然!

反射看來根本不區分是否是private的,調用本身的私有方法是可以的,但是調用父類的私有方法則不行,糾其原因很有可能是因為getDeclaredMethod方法和getMethod方法並不會查找父類的私有方法,自己寫遞歸可以解決,不過利用反射來做的話性能不會太好。

我們來看下面這個代碼。

[java]
  1. Field[] fields = obj.getDeclaredFields();  
  2.             for (int i = 0; i < fields.length; i++) {  
  3.                 fields[i].setAccessible(true);  
  4.                 for (int j = 0; j < args.length; j++) {  
  5.                     String str = args[j];  
  6.                     String strs[] = str.split(",");  
  7.                     if (strs[0].equals(fields[i].getName())) {  
  8.                         fields[i].set(object, strs[1]);  
  9.                         break;  
  10.                     }  
  11.                 }  
  12.             }  
[java]
  1. fields[i].setAccessible(true);  

這句話是關鍵。看它的表面英文意思是設置可進入可訪問為:true。編程意思大家猜想也應該知道了。

通過查看JDK的源碼:

[java]
  1. public void setAccessible(boolean flag) throws SecurityException {  
  2.     SecurityManager sm = System.getSecurityManager();  
  3.     if (sm != null) sm.checkPermission(ACCESS_PERMISSION);  
  4.     setAccessible0(this, flag);  
  5.     }  

我們可以看到它是通過SecurityManager來管理權限的,我們可以啟用java.security.manager來判斷程序是否具有調用setAccessible()的權限。默認情況下,內核API和擴展目錄的代碼具有該權限,而類路徑或通過URLClassLoader加載的應用程序不擁有此權限。例如:當我們以這種方式來執行上述程序時將會拋出異常 

[java]
  1. java.security.AccessControlException:   access   denied   

 

一般情況下,我們並不能對類的私有字段進行操作,但有的時候我們又必須有能力去處理這些字段,這時候,我們就需要調用AccessibleObject上的setAccessible()方法來允許這種訪問,而由於反射類中的Field,Method和Constructor繼承自AccessibleObject,因此,通過在這些類上調用setAccessible()方法,我們可以實現對這些字段的操作。


我們來看看這個ACCESS_PERMISSION里面究竟怎么處理的:

[java]
  1. static final private java.security.Permission ACCESS_PERMISSION =  
  2. new ReflectPermission("suppressAccessChecks");  

查找JDK幫助文檔可以看到詳細解釋:


public final class ReflectPermissionextends BasicPermission

反射操作的 Permission 類。ReflectPermission 是一種指定權限,沒有動作。當前定義的唯一名稱是suppressAccessChecks,它允許取消由反射對象在其使用點上執行的標准 Java 語言訪問檢查 - 對於 public、default(包)訪問、protected、private 成員。


下表提供了允許權限的簡要說明,並討論了授予代碼權限的風險。

權限目標名稱 權限允許的內容 允許此權限的風險

suppressAccessChecks

能夠訪問類中的字段和調用方法。注意,這不僅包括 public、而且還包括 protected 和 private 字段和方法。

存在的風險是,通常不可用的信息(也許是保密信息)和方法可能會接受惡意代碼訪問。


這里就一點了然了。fields.setAccessible(true);的實際作用就是使權限可以訪問public,protected,private的字段!

是不是很爽呢。當然這種方法破壞了JAVA原有的權限體系。所以不到萬不得已,還是少用,反射的效率畢竟不是那么高滴。

好,知道了這個我們再來寫一個通用的萬能方法,只是傳遞相應的類,字段名稱和值,我們在方法內部將其反射並進行實例化。然后進行相應字段的賦值。由於我只用到了字段。你可以加上其它的東東。嗯。這個好玩。

[java]
  1. package unit.sms;  
  2.   
  3. public class Smss {  
  4.     private String destID;  
  5.     private String content;  
  6.     private String mobile;  
  7.     public String getDestID() {  
  8.         return destID;  
  9.     }  
  10.     public String getContent() {  
  11.         return content;  
  12.     }  
  13.     public String getMobile() {  
  14.         return mobile;  
  15.     }  
  16.       
  17. }  


[java]
  1. package com.sinoglobal.utils;  
  2.   
  3. import java.lang.reflect.Field;  
  4.   
  5. import com.jasson.mas.api.smsapi.Sms;  
  6.   
  7. /** 
  8.  * 反射的通用工具類 
  9.  *  
  10.  * @author lz 
  11.  *  
  12.  */  
  13. public class ReflectionUtils {  
  14.       
  15.     /** 
  16.      * 用於對類的字段賦值,無視private,project修飾符,無視set/get方法 
  17.      * @param c 要反射的類 
  18.      * @param args 類的字段名和值 每個字段名和值用英文逗號隔開 
  19.      * @return 
  20.      */  
  21.     @SuppressWarnings("unchecked")  
  22.     public static Object getInstance(Class c, String... args) {  
  23.         try {  
  24.             Object object = Class.forName(c.getName()).newInstance();  
  25.             Class<?> obj = object.getClass();  
  26.             Field[] fields = obj.getDeclaredFields();  
  27.             for (int i = 0; i < fields.length; i++) {  
  28.                 fields[i].setAccessible(true);  
  29.                 for (int j = 0; j < args.length; j++) {  
  30.                     String str = args[j];  
  31.                     String strs[] = str.split(",");  
  32.                     if (strs[0].equals(fields[i].getName())) {  
  33.                         fields[i].set(object, strs[1]);  
  34.                         break;  
  35.                     }  
  36.                 }  
  37.             }  
  38.             return object;  
  39.         } catch (IllegalAccessException e) {  
  40.             e.printStackTrace();  
  41.         } catch (ClassNotFoundException e) {  
  42.             e.printStackTrace();  
  43.         } catch (InstantiationException e) {  
  44.             e.printStackTrace();  
  45.         }  
  46.         return null;  
  47.     }  
  48.     public static void main(String[] args) {  
  49.         Object object = getInstance(Smss.class, "destID,08340001", "mobile,13408340000", "content,ReYo測試數據。");  
  50.                 Smss sms = (Smss) object;  
  51.                 System.out.println("短信內容:" + sms.getContent());  
  52.                 System.out.println("手機號碼:" + sms.getMobile());  
  53.                 System.out.println("尾號:" + sms.getDestID());  
  54.     }  
  55. }  


控制台輸出:

短信內容:ReYo測試數據
手機號碼:13408340000
尾號:08340001


fields.setAccessible(true);的使用可能大家都會,但我們要做的是,知其然,知其所以然。

看JDK的源碼,無疑是學習和解決此方法的最佳途徑。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM