java基礎——反射
JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。
Class類和java.lang.reflect類庫一起對反射機制進行了支援,該類庫包含了Field、Method以及Constructor類。下面就從這兩大方面對反射進行介紹:
1. Class類
類作為程式的一部分,每個類都擁有一個Class物件。每當編寫並編譯一個新類,就會產生一個Class物件(儲存在一個同名的.class檔案中)。為了生成這個類的物件,jvm使用了類載入器系統。
所有的類都是在第一次使用時,動態載入到jvm中的。類載入器首先檢查這個類的Class物件是否已經載入,未載入就會查詢.class檔案(本地或網路獲取)。
1.1 Class類獲取
以下三種方式均可以獲取Class類:
return reportInfo;
Integer num = new Integer(123);
Class c1 = num.getClass();
Class c2 = Integer.class;
Class c3 = Class.forName("java.lang.Integer");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class cl4 = loader.loadClass("java.lang.Integer");
注意點
- .class方式不會引起類的初始化,而Class.forName會引起對應類進行初始化。
- 基本的 Java 型別(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也都對應一個 Class 物件。
- 每個陣列屬於被對映為 Class 物件的一個類,所有具有相同元素型別和維數的陣列都共享該 Class 物件。
1.2 Class提供常用方法
- getClassLoader() :返回該類的類載入器
- isArray() :判斷是否是陣列
- getName():以 String 的形式返回此 Class 物件所表示的實體(類、介面、陣列類、基本型別或 void)名稱
- newInstance():可以建立該類的示例
- 同時提供了獲取Constructor、Method和Field的方法,後續會詳細說明
1.3 Class使用技巧
- forName和newInstance結合起來使用,可以根據儲存在字串中的類名建立物件,例如:Object obj = Class.forName("xxxx").newInstance();
- 虛擬機器為每種型別管理一個獨一無二的Class物件。因此可以使用==操作符來比較類物件,例如:if(e.getClass() == Employee.class)
2. reflect包
reflect包中有三個類,Field,Method,Constructor,分別去描述類的域,方法,構造器。
通過Class類可以分別獲取上述三個類的具體例項,下面進行分別講述:
2.1 Field類
表示類的成員變數,其中一個成員變數對應一個Field物件。Class物件獲取Field方法如下:
- getFields():獲得類的public型別的屬性
- getDeclaredFields():獲得類的所有屬性
- getField(String name):獲取指定名稱public型別屬性
- getDeclaredFields(String name):獲取指定名稱屬性
通過上述4種方法,我們可以獲取指定的Field物件。通過Field物件我們可以實現以下常見功能:
- String getName():獲取欄位名
- Class<?> getType() 和 Type getGenericType() :獲取型別和泛型
- int getModifiers():獲取修飾
- Object get(Object obj):獲取指定物件該欄位對應值
- void set(Object obj, Object value):給指定物件的該段賦值
這裡演示一下如何修改private的屬性值:
這裡需要注意的是在賦值給private屬性之前需要呼叫field.setAccessible(true)方法關閉對private屬性訪問檢查。
2.2 Method類
表示類的成員方法,其中一個成員方法對應一個Method物件。Class物件獲取Method方法與Filed類似如下:
- getMethods():獲得類的public型別的方法
- getDeclaredMethods():獲得類的所有方法
- getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name引數指定方法的名字,parameterTypes 引數指定方法的引數型別,同時該方法為public的
- getDeclaredMethod(String name, Class<?>... parameterTypes):同上只是範圍擴大到所有方法
同樣獲取Method物件以後,我們可以實現以下常見功能:
- Class<?>[] getParameterTypes():獲取該方法的所有引數型別
- Class<?> getReturnType():獲取該方法返回值
- <T extends Annotation> T getAnnotation(Class<T> annotationClass):獲取方法註解
- Object invoke(Object obj, Object... args)執行該方法
同樣這裡演示如何執行一個private方法:
同樣在執行private方法之前需要執行method.setAccessible(true),關閉對private方法訪問檢查。
2.3 Constructor類
表示類的構造方法,其中一個構造方法對應一個Constructor物件。Class獲取Constructor的方法如下:
- getConstructors():獲得類的public型別的構造方法
- getDeclaredConstructors():獲取所有構造方法
- getConstructor(Class<?>... parameterTypes):獲得類的特定public構造方法,parameterTypes 引數指定構造方法的引數型別。
- getDeclaredConstructor(Class<?>... parameterTypes):同上範圍擴大到所有構造方法
同樣獲取Constructor物件以後,我們可以實現以下常見功能:
- Class<?>[] getParameterTypes():構造方法引數列表
- T newInstance(Object ... initargs):例項化對應類
接下來示例展示如何利用Constructor物件例項化對應類
2.4 反射與泛型
常見兩種泛型使用場景:
- 宣告一個需要被引數化(parameterizable)的類/介面,例如:class MyClass<T>
- 使用一個引數化類,例如:List<String> arrays;
2.4.1Type介面
在此之前我們需要介紹一下Type介面,Java程式語言中所有型別的公共父介面,這裡所說的所有型別包括:
原始型別 (raw types)對應Class,引數化型別 (parameterizedtypes)對應ParameterizedType, 陣列型別 (array types)對應GenericArrayType,型別變數 (type variables)對應TypeVariable,基本資料型別(primitivetypes)仍然對應Class。
Class類實現了該介面,同時該介面的直接子介面包括以下4個:
- ParameterizedType: 表示一種引數化的型別,比如Collection<String>
- GenericArrayType: 表示一種元素型別是引數化型別或者型別變數的陣列型別
- TypeVariable: 是各種型別變數的公共父介面
- WildcardType: 代表一種萬用字元型別表示式
配合後面的反射內容重點介紹一下java.lang.reflect.ParameterizedType介面
- 含義:表示引數化型別比如:Map<String, Date>這種引數化型別
- Type[] getActualTypeArguments():獲取引數化型別<>中的實際型別(注意對於多次巢狀,該方法只返回脫去最外層的<>以後剩下內容返回)
- 關於返回值是陣列因為存在Map<String, Date>返回不止一種型別
- 為什麼返回父介面,因為返回值多型性,對於ArrayList<ArrayList<Integer>>返回的是ArrayList<Integer>是ParameterizedType型別的,而對於ArrayList<E>返回的是E是TypeVariable型別,而對於 對於ArrayList<E[]>返回的是E[]則對應的是GenericArrayType型別。
- Type getRawType():獲取承載該泛型資訊的物件,該型別表示<>前面的型別比如Map<String,String>,則返回Map
2.4.2引數化型別反射獲取
講述完Type相關內容以後,我們來看下Field、Method和Constructor相關類如何獲取泛型引數或泛型返回值。這裡我們以Method為例說明:
在這個示例中我們重點關注一下method.getGenericReturnType()方法,返回返回值Type,檢視jdk原始碼:
觀察程式碼可以發現在獲取泛型返回是首先判斷getGenericSignature()是否為空該函式是native的沒找到相關資料,從名稱推斷是標識是否是泛型,在Field相關原始碼中也有出現。如果返回不問空則返回ParameterizedType型別,否則呼叫getReturnType返回Class<?>型別。
檢視Field、Constructor相關程式碼可以看出相似使用方法:
- Field類中屬性型別 Class<?> getType()和Type getGenericType()
- Method類中方法返回值型別Class<?> getReturnType() 和Type getGenericReturnType()
- Method類中方法引數型別Class<?>[] getParameterTypes()和Type[] getGenericParameterTypes()
- Constructor類中方法引數和Method中方法引數完全一致
最後,如果需要解析Field、Method、Constructor中泛型相關型別,使用步驟和給出示例類似:
- 呼叫相關Genric對應的方法
- 判斷返回值instanceof ParameterizedType
- 步驟為true強制轉換為ParameterizedType型別
- 後續呼叫getActualTypeArguments等方法獲取真實引數
參考文件
http://lavasoft.blog.51cto.com/62575/15433/
http://developer.51cto.com/art/201103/250028.htm
http://blog.csdn.net/benjaminzhang666/article/details/9838937
相關文章
- Java反射—初探反射基礎Java反射
- 【Java 反射學習】Java 反射基礎Java反射
- [Java 反射學習] Java 反射基礎Java反射
- Java 反射基礎Java反射
- [Java基礎]反射Java反射
- Java基礎 —— 反射Java反射
- Java基礎系列—Java反射Java反射
- Java基礎(十八)反射Java反射
- Java基礎(十一)——反射Java反射
- Java-基礎-反射Java反射
- 【Java反射】Java 泛型基礎Java反射泛型
- java基礎 -反射筆記Java反射筆記
- Java基礎——深入理解反射Java反射
- Java基礎之反射機制(續)Java反射
- Java重點基礎:反射機制Java反射
- JAVA基礎學習篇之反射Java反射
- 反射基礎反射
- 基礎篇:深入解析JAVA反射機制Java反射
- Java基礎對反射知識總結Java反射
- java框架基礎技術之--------反射機制Java框架反射
- Java程式設計基礎28——反射&JDK新特性Java程式設計反射JDK
- java基礎語法(三十九)—反射機制(二)Java反射
- java基礎:深入理解Class物件與反射機制Java物件反射
- Java基礎加強筆記——測試、反射、註解Java筆記反射
- Java安全基礎之Java反射機制和ClassLoader類載入機制Java反射
- 【Go進階—基礎特性】反射Go反射
- java EE開發之Servlet第十一課:反射基礎三JavaServlet反射
- PHP DIY 系列------基礎篇:3. 反射PHP反射
- Java基礎-語法基礎Java
- JAVA基礎加強篇12——單元測試、反射、註解、動態代理Java反射
- 探索基礎之反射——第一章反射
- JAVA 基礎Java
- java基礎Java
- [Java基礎]Java
- Java 反射Java反射
- Java——反射Java反射
- Java反射Java反射
- Java 基礎02Java程式設計基礎Java程式設計
- Java基礎-物件導向基礎Java物件