詳解Java反射機制(Reflection)
反射機制的作用
JAVA反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和屬性,實現了基本的動態性。
詳解反射機制
類的組成
正如我們所知道的,一個類的組成包括了一下幾個部分:類名、構造器、方法、域、注解,所以為了能夠獲得任意一個類的對象,則需要能夠獲得該類的全部組成,JDK中的Reflection包為我們提供了一下幾個對應類的各個組成部分的類,分別是Class
, Construtor
,Method
, Field
,下面我們詳細地了解各個 "組成類"。
-
Class
- Class類是一個比較特殊的類,之所以說它特殊,是因為在每個Java對象在加載到JVM中之后,都會產生一個Class的對象,用來跟蹤該對象所代表的類。下面我們做一個小實驗
public void test(){ String str1 = "I love Java"; String str2 = new String("I Lov Java"); System.out.println(str1.getClass()); System.out.println(str2.getClass()); System.out.println(String.class); }
輸出的結果分別如下:
class java.lang.String class java.lang.String class java.lang.String
上面的結果說明了,String的任意不同對象在JVM中只有一個Class對象,也就是說Class對象在JVM中只有唯一一份。
- 獲得Class對象的實例
在實際的應用中,有三種方式可以獲得Class對象,如下代碼所示:(這里為了下文的方便,我創建了一個Person類,具有name,age兩個屬性域,以及生成對應的set,get方法)
String className = "cn.xuhuanfeng.reflection.Person"; Class<?> clazz1 = Class.forName(className); // 獲得Class對象 Class<Person> clazz2 = Person.class; // 獲得Class對象 Class<? extends Person> clazz3 = (new Person()).getClass(); // 獲得Class對象
創建實例
獲得了Class的對象之后,我們就可以利用它來創建類的實例,從而實現在運行時創建類,如下代碼所示:
Person person = (Person)clazz2.newInstance();
這樣我們就獲得了一個Person對象,不過這里要注意的是,Class的`newInstance()` 方法只能使用無參構造函數創建,也就是說,只有當該類具有無參構造方法時,才能使用這種方法來獲得一個實例,這看上去比較無奈,不過也不用太擔心,通過下面將介紹到的Construtor的`newInstance()` 方法即可以使用該類的有參夠構造方法來獲得實例。
- 獲得類名
有時候我們需要獲得類的類名,雖然不是總是需要,我們可以通過下面的方法來獲得
System.out.println(clazz1.getName());//cn.xuhuanfeng.reflection.Person
System.out.println(clazz1.getSimpleName());//Person
相信大家可以看出上面兩者的區別,這里就不進行敘述。
-
Construtor
正如Class表述一個類的總體情況一樣,Construtor描述的是一個類的構造器類,沒錯,構造器也是一種類。
Constructor[] construtor = clazz.getConstructors(); //獲得Public類型的構造器
Constructor[] construtor2 = clazz.getDeclaredConstructors(); // 獲得所有類型的構造器
construtor以及construtor2的內容輸出如下:
``` Java
// construtor
public cn.xuhuanfeng.reflection.Person()
public cn.xuhuanfeng.reflection.Person(java.lang.String,int)
// construtor2
public cn.xuhuanfeng.reflection.Person()
public cn.xuhuanfeng.reflection.Person(java.lang.String,int)
這里由于Person只有public類型的構造器,所以兩者包含的內容相同。
當然我們還可以通過制定參數類型來獲得特定的構造器
Constructor construtor3 = clazz.getConstructor(String.class,int.class);
上面我們提到了通過構造器來獲得帶參數類型的實例,其實就是通過上面的方式獲得帶參數的構造器對象,然后調用器newInstance(parameter ...)
方法即可,如下所示:
Person person1 = (Person)construtor3.newInstance("xuhanfeng",23);
這樣,我們就實現了通過任意構造器創建對象。
-
Method
Method 描述的是一個類的方法,同上面的Construtor類似,這里我們不進行過多的解釋,直接看代碼演示。
Method[] methods = clazz.getMethods(); // 獲得所有的public方法
Method[] methods2 = clazz.getDeclaredMethods(); // 獲得所有的方法
Method method3 = clazz.getDeclaredMethod("setName", String.class);// 獲得指定的方法
看到這里,相信你會發現,基本上跟前面的Construtor是類似,不過這里也有點不用,就是調用方法的時候,使用的是invoke(obj,params)
方法,如下
method3.invoke(person, "xuhuanfeng");// person 為前面獲得的實例
這樣,我們就實現了調用任意方法了。
-
Field
Field是用來描述一個類所有的域的類,相信經過前面的Construtor以及Method,對于Field我們已經不用再進行過多解釋了,直接看代碼
Field[] fields = clazz.getFields(); // 獲得所有的public的域
Field[] fields2 = clazz.getDeclaredFields(); // 獲得所有的域
Field field3 = clazz.getDeclaredField("name"); // 獲得指定的域
這里同樣有個需要注意的地方,由于Java的安全機制原因,當我們要操作非public類型的域的時候,需要設置暫時關閉Java的安全檢驗,如下:
field3.setAccessible(true); //關閉安全校驗
之后我們就能對field3進行設置值了
field3.set(person, "xuhuanfeng"); //person同上
這樣,我們就能實現操作任意域了。
-
Annotation
相信經過上面的例子,你已經比較了解了,所以對于Annotation這里就不再進行闡述了,操作跟上面基本都是類似的。
后記
Java的反射機制給開發者帶來了極大的便利,很多的框架也正是利用Java的反射機制從而實現了強大的生命力,相信看到這里,對于Java的反射,你已經有比較好的認識了。