定義
它允許運行中的 Java 程序獲取自身的信息,並且可以操作類或對象的內部屬性。通過反射,我們可以在運行時獲得程序或程序集中每一個類型的成員和成員的信息。程序中一般的對象的類型都是在編譯期就確定下來的,而 Java 反射機制可以動態地創建對象並調用其屬性,這樣的對象的類型在編譯期是未知的。所以我們可以通過反射機制直接創建對象,即使這個對象的類型在編譯期是未知的。
反射的核心是 JVM 在運行時才動態加載類或調用方法/訪問屬性,它不需要事先(寫代碼的時候或編譯期)知道運行對象是誰。
Java 反射主要提供以下功能:
- 在運行時判斷任意一個對象所屬的類;
- 在運行時構造任意一個類的對象;
- 在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調用private方法);
- 在運行時調用任意一個對象的方法
重點:是運行時而不是編譯時
反射的主要用途
很多人都認爲反射在實際的 Java 開發應用中並不廣泛,其實不然。當我們在使用 IDE(如 Eclipse,IDEA)時,當我們輸入一個對象或類並想調用它的屬性或方法時,一按點號,編譯器就會自動列出它的屬性或方法,這裏就會用到反射。
反射最重要的用途就是開發各種通用框架。很多框架(比如 Spring)都是配置化的(比如通過 XML 文件配置 Bean),爲了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法,這個時候就必須用到反射,運行時動態加載需要加載的對象。
舉一個例子,在運用 Struts 2 框架的開發中我們一般會在 struts.xml
裏去配置 Action
,比如:
1 2 3 4 5 6 |
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action> |
配置文件與 Action
建立了一種映射關係,當 View 層發出請求時,請求會被 StrutsPrepareAndExecuteFilter
攔截,然後 StrutsPrepareAndExecuteFilter
會去動態地創建 Action 實例。比如我們請求 login.action
,那麼 StrutsPrepareAndExecuteFilter
就會去解析struts.xml文件,檢索action中name爲login的Action,並根據class屬性創建SimpleLoginAction實例,並用invoke方法來調用execute方法,這個過程離不開反射。
對與框架開發人員來說,反射雖小但作用非常大,它是各種容器實現的核心。而對於一般的開發者來說,不深入框架開發則用反射用的就會少一點,不過了解一下框架的底層機制有助於豐富自己的編程思想,也是很有益的。
1、獲得 Class 對象
方法有三種:
(1) 使用 Class 類的 forName
靜態方法:
1 2 3 4 5 6 7 |
public static Class<?> forName(String className) ``` 比如在 JDBC 開發中常用此方法加載數據庫驅動: ```java Class.forName(driver); |
(2)直接獲取某一個對象的 class,比如:
1 2 |
Class<?> klass = int.class; Class<?> classInt = Integer.TYPE; |
(3)調用某個對象的 getClass()
方法,比如:
1 2 |
StringBuilder str = new StringBuilder("123"); Class<?> klass = str.getClass(); |
2、判斷是否爲某個類的實例
一般地,我們用 instanceof
關鍵字來判斷是否爲某個類的實例。同時我們也可以藉助反射中 Class 對象的 isInstance()
方法來判斷是否爲某個類的實例,它是一個 native 方法:
1 |
public native boolean isInstance(Object obj); |
3、創建實例
通過反射來生成對象主要有兩種方式。
- 使用Class對象的newInstance()方法來創建Class對象對應類的實例。
1 2 |
Class<?> c = String.class; Object str = c.newInstance(); |
- 先通過Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建實例。這種方法可以用指定的構造器構造類的實例。
1 2 3 4 5 6 7 |
//獲取String所對應的Class對象 Class<?> c = String.class; //獲取String類帶一個String參數的構造器 Constructor constructor = c.getConstructor(String.class); //根據構造器創建實例 Object obj = constructor.newInstance("23333"); System.out.println(obj); |
4、獲取方法
獲取某個Class對象的方法集合,主要有以下幾個方法:
getDeclaredMethods
方法返回類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。
1 |
public Method[] getDeclaredMethods() throws SecurityException |
getMethods
方法返回某個類的所有公用(public)方法,包括其繼承類的公用方法。
1 |
public Method[] getMethods() throws SecurityException |
getMethod
方法返回一個特定的方法,其中第一個參數爲方法名稱,後面的參數爲方法的參數對應Class的對象。
1 |
public Method getMethod(String name, Class<?>... parameterTypes) |