成鵬致遠 | lcw.cnblog.com |2014-02-04
反射機制
1.認識Class類
- 在正常情況下,必須知道一個類的完整路徑之後才可以例項化物件,但是在 java中也允許通過一個物件來找到其所在的類的資訊,那麼這實際上就是 Class類的功能
- 此時,所有的操作都是反著來的
- Object類的支援
- 在Object類中定義了以下的方法,此方法將被所有子類繼承:public final Class getClass()
- 以上的方法返回值的型別是一個“Class”類,實際上此類是 Java反射的源頭,實際上所謂反射就是:可以通過物件反射求出類的名稱
- Class類
- Class本身表示一個類的本身,通過 Class可以完整的得到一個類中的完整結構,包括此類中的方法定義,屬性定義等
- 此類在 JDK文件中沒有發現任何的構造方法,所以此類的構造方法被私有化了
- 例項化 Class類物件,方法有三種
- 第一種:通過 forName()方法
- 第二種:類.class
- 第三種:物件.getClass()
- 一旦可以例項化 Class類之後,就可以進行反射的進一步操作
- 小結
- 瞭解 Class類的作用:反射的源頭
- 三個 Class類的例項化方式,其中要以“forName()”重點掌握,類.class
2.Class類的作用
- Class主要是反射的源頭,不光可以取得物件所在類的資訊,也可以直接通過 Class類的方法進行物件的例項化操作,正常情況下,使用關鍵字 new為物件例項化,如果現在已經例項化好了 Class物件,則就可以通過 Class類中提供的 newInstance()例項化物件
- 即使不使用關鍵字 new物件也可以進行例項化操作,反射的作用
- 【注意】在操作中類中必須存在無參構造方法,否則無法例項化
- 一般使用反射例項化物件的時候,類中都最好存在一個無參構造,這樣的操作比較合理
- 如果要想呼叫有參構造,則必須按照以下的步驟進行:
- 通過 Class類中的 getConstructors()取得本類中的全部構造方法
- 向構造方法中傳遞一個物件陣列進去,裡面包含了構造方法中所需的各個引數
- 之後通過 Constructor例項化物件
- 小結
- 本節的功能是 Class用的最多的功能,而且在開發中使用者也會經常使用到的開發模式
- 在使用 Class例項化物件的時候,必須保證類中存在一個無參構造,否則無法使用
3.反射應用:取得類的結構
- 通過反射得到一個類的完整結構,需要使用到 java.lang.reflect包中的幾個類
- Constructor:表示類中的構造方法
- Field:表示類中的屬性
- Method:表示類中的方法
- 取得一個類所實現的全部介面
- 使用 Class類中的 getInterfaces()方法
- 此方法返回一個 Class類的物件陣列,之後就可以直接利用 Class類中的 getName()方法輸出即可
- 取得類所繼承的父類
- 一個類可以實現多個介面,但是隻能繼承一個父類
- 如果要想取得一個類的父類,可以直接使用 Class類中的 getSuperclass()方法
- 此方法返回的是 Class例項,和之前得到的介面一樣,可以通過 敷衍Name()方法取得名稱
- Constructor類中的幾個方法
- 取得修飾符:public int getModifiers()
- 取得方法名稱:public String getName()
- 取得引數的型別:public Class<?>[] getParameterTypes()
- 還原修飾符:直接使用 Modifier類中的 public static String toString(int mod)
- 取得類中的方法
- 要想取得一個類的全部方法,可以使用 Class類中的 getDeclaredMethods()方法
- 此方法返回一個 Method類的物件陣列,而如果要想進一步取得方法的具體資訊,例如:方法的引數,丟擲的異常宣告等等,則就必須依靠 Method類
- 輸出本類中的全部方法:public Method[] getDeclaredMethods() throws SecurityException
- 輸出全部的方法:public Method[] getMethods() throws SecurityException
- 取得全部的返回值:public Class<?> getReturnType()
- 取得全部的引數:public Class<?> getParameterTypes()
- 取得修飾符:public int getModifiers()
- 取得異常資訊:public Class<?>[] getExceptionTypes()
- 在一般的開發工具中經常看見隨筆提示功能,實際上此功能就是利用以上函式完成的
- 取得類中的屬性
- 得到實現的介面或父類中的公共屬性:public Field[] getFields() throws SecurityException
- 得到本類中的全部屬性:public Field[] getDeclaredFields() throws SecurityException
- 以上方法返回的都是 Field的陣列,每一個 Field物件就表示類中的一個屬性
- 小結
- 在實際的操作中,取得類的資訊的操作程式碼使用者並不會經常開發
- 一定要熟悉 java.lang.reflect包的作用,反射機制
- 如何取得屬性、方法、構造的名稱,修飾符等等
4.Java反射機制深入研究
- 通過反射呼叫類中的方法
- 在正常情況下一個類的物件產生之後,就可以直接呼叫類中的方法了,如果要想呼叫的話,則肯定必須清楚的知道要呼叫的方法名稱是什麼,之後通過 Class類中的 public Method getMethod(String name, Class<?>... parameterTypes)
- 得到一個方法的 Method物件是,之後通過此 Method物件來執行方法(invoke()),但是在方法呼叫的時候,因為會牽扯到方法中的引數的問題,所以通過 getMethod()取得的時候,必須設定需要的引數型別
- 執行的時候還需要傳遞引數進去,而且還需要例項化物件
- 通過反射呼叫類 setter及 getter
- setter及 getter方法是一個標準的屬性的訪問方法,如果一個類的屬性被封裝,則必須通過 setter及 getter方法設定和取得
- 實際上此方法的操作之所以要這樣規定,主要原因是由於反射機制可以給予支援的
- 通過反射呼叫屬性
- 如果要操作一個類中的屬性,則可以通過 Filed完成,而不必麻煩的通過 setter及 getter
- 在訪問私有屬性的時候,必須讓這個屬性可見:public void setAccessible(boolean flag) throws SecurityException,將其內容設定成 true即可
- 此操作呼叫與 setter及 getter無關,但是為了保證程式的安全性,最好還是通過 setter及 getter方法完成呼叫
- 通過反射運算元組
- 反射機制不光只能用在類中,也可以應用在任意的引用資料型別上,當然這就包含了陣列,陣列使用 Array類完成
- Class類中存在一個方法:public Class<?> getComponentType()
- 得到陣列指定下標的內容:get(Object array, int index)
- 修改內容:set(Object array ,int index)
- 開闢新陣列:newInstance(Class<?> componentType, int... dimensions)
- 小結
- 重點理解 setter及 getter方法的呼叫問題,原理,程式碼最好可以清楚的掌握
- 使用反射操作屬性是不建議直接使用的
5.動態代理
- 代理設計:一個操作的介面有兩個子類,其中一個是真實主題的實現類,另外一個是代理類,代理實現類要完成比真實主題實現類更多的內容,而且本身還需要處理一些與具體業務有關的程式程式碼
- 之前寫過的代理實際上稱為靜態代理,因為一個代理類只能為一個介面服務,那麼如果現在有很多個介面的話,那麼則肯定代理類就很多了;而且所有的代理操作除了呼叫的方法不一樣之外,其它的操作都一樣,則此時肯定是重複的程式碼
- InvocationHandler介面:public interface InvocationHandler{public Object invoke(Object proxy, Method method, Object[] args) throws Throwable}
- Object proxy:被代理的物件
- Method method:要呼叫的方法
- Object args[]:方法呼叫時所需要的引數
- 可以將 InvocationHandler介面的子類想象成一個代理的最終操作類,替換掉 ProxySubject
- Proxy類
- Proxy類是專門完成代理的操作類,可以通過此類為一個或多個介面動態地生成實現類
- public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHandler h) throws IIIlegalArgumentException
- ClassLoader loader:類載入器
- Class<?>[] interfaces:得到全部的介面
- InvocationHandler h:得到 InvocationHandler介面的子類例項
- 類載入器
- 在 Proxy類中的 newProxyInstance()方法中需要一個 ClassLoader類的例項,ClassLoader實際上對應的是類載入器
- 在Java中主要有以下三種類載入器
- Bootstrap ClassLoader:此載入器採用 C++編寫,一般開發中是看不到的
- Extension ClassLoader:用來進行擴充套件類的載入,一般對應的是 jre\lib\ext目錄中的類
- AppClassLoader:載入 classpath指定的類,是最常使用的一種載入器
- 如果要想得到一個載入器的物件,則肯定使用 class類完成
- 小結
- 使用動態代理,可以完成代理的功能,而且可以代理全部的介面
6.高階工廠設計模式
- 工廠設計模式最大的好處是可以在應用進行解耦合操作
- 之前寫過的工廠設計模式存在的問題:如果想擴充一個子類,則肯定要修改工廠類,如果希望在擴充子類時不用修改工廠類的話,則就必須使用反射完成
- 如果直接使用反射,則需要輸入完整的“包.類”名稱,肯定很麻煩,所以,此時可以通過一些配置檔案的方式儲存這些完整的類路徑
- 所以此時可以通過屬性檔案的形式配置所要的子類資訊
- 程式執行的時候,就可以通過屬性檔案的內容讀取出來,之後直接操作屬性檔案的 key,就可以避免輸入過長的類路徑
- 配置檔案與程式程式碼相分離,這種設計思路是以後開發基本思路,當然,最新的設計理念:是在程式中直接通過註釋的方式進行配置
- 小結
- 反射機制對程式開發所帶來的好處
- 程式程式碼與配置檔案相分離的理論
Annotation
1.系統內建Annotation
- Annotation:在 JDK1.5之後增加的一個新特性,這種特性被稱為後設資料特性,在 JDK1.5之後稱為註釋,即:使用註釋的方式加入一些程式的資訊
- java.lang.annotation.Annotation介面是所有的 Annotation都必須實現的介面
- 在 JDK1.5之後,系統中已經建立了如下的三個內建的 Annotation型別,使用者直接使用即可
- @Override:覆寫的 Annotation
- @Deprecated:不贊成使用的 Annotation
- @SuppressWarnings:壓制安全警告的 Annotation
- @Override:表示方法覆寫的正確性,使用 @Override註釋可以保證程式正確的執行
- @Deprecated:使用 @Deprecated註釋的 Annotation本身是不建議使用的一個操作
- @SuppressWarnings:用於壓制警告資訊
- 可以壓制一個警告資訊,也可以同時壓制多個警告資訊,只需要以陣列的形式出現即可
- 在設定註釋資訊的時候,是以 key->value的形式出現的,所以 @SuppressWarnings也可以直接使用“value={"unchecked","deprecation"}”的方式設定
- 小結
- 系統內建的三個 Annotation的作用,可以發現通過註釋可以完成一些程式碼的其它功能
2.自定義Annotation
- 定義簡單的 Annotation:[public] @interface Annotation 名稱{資料型別 變數名稱();}
- 之後就可以直接在程式中使用“@Annotation 名稱”的格式了
- 還可以向 Annotation中設定引數,使用變數接收引數
- 在使用時就必須清楚的指定變數的內容
- 也可以使用明確的標記,表明內容賦給哪個引數
- 也可以設定多個引數
- 還可以設定一個陣列進去
- 以上全部的 Annotation中有一個特點,所有的引數內容需要在使用註釋的時候設定上去,那麼也可以為一個引數設定預設的內容,在宣告的時候使用 default即可,當再去使用此 Annotation的時候就可以不用設定內容
- 在操作中,對於一個 Annotation而言,有時候會固定其取值範圍,只能取固定的幾個值,那麼這個時候實際上就需要依靠列舉,那麼外部使用此 Annotation的時候也需要從列舉固定取值
- Retention和RetentionPolicy
- 在 Annotation,可以使用 Retention定義一個 Annotation的儲存範圍
- Retention定義中存在了一個 RetentionPolicy的變數,此變數用於指定 Annotation的儲存範圍,RetentionPolicy包含三種範圍
- 在三個範圍中,最需要關心的就是 RUNTIME範圍,因為在執行時候起作用,在以後反射與 Annotation的操作中,也必須使用此種範圍
- 內建 Annotation的 RetentionPolicy
- 三個內建的 Annotation
- Override:定義採用的是 @Retention(value=SOURCE),只能在原始檔中出現
- Deprecated:定義採用的是 @Retention(value=RUNTIME),可以在執行時出現
- SuppressWarnings:定義採用的是 @Retention(value=SOURCE),只能在原始檔中出現
- 小結
- Annotation定義格式,及變數的型別:列舉、陣列、普通變數
- Retention、RetentionPolicy的作用
3.反射與 Annotation
- 一個 Annotation如果要想讓其變得有意義,則必須結合反射機制取得 Annotation中設定的全部內容
- 系統內建的三個 Annotation,只有 Deprecated的 Annotation的定義範圍是 RUNTIME範圍
- 在 Class類中存在與 Annotation操作有關的方法
- 如果在一個元素中存在註釋,則取得全部註釋:getAnnotation()
- 判斷一個元素上是否存在註釋:isAnnotationPresent()
- 小結
- Annotation在實際的開發中,不管如何使用,其最終肯定是結合反射機制
- 也就是說可以通過 Annotation設定一些內容到一個方法上去,以完成特定的功能
4.深入Annotation
- 之前定義的 Annotation,如果沒有明確的說明可以在任意的位置上使用
- 如果現在需要指定其使用的範圍的話,則就必須使用 @Target註釋
- ElementType的儲存範圍
- 如果希望一個 Annotation可以在類及方法上同時使用的話,則必須設定多個範圍
- @Documented:可以在任何的 Annotation上使用
- 所有的 Annotation預設情況下都是使用 @Document進行註釋的
- 生成的 javadoc的時候可以通過 @Document設定一些說明資訊
- @Inherited:此註釋表示一個 Annotation是否可以被繼承下來
- 如果在父類上使用 @Inherited,則其子類可以將此 Annotation繼承下來
- 小結
- 熟悉 Documented註釋的作用:加入說明資訊
- 熟悉 Target的作用,並使用 Target註釋指定註釋的使用位置
- 如果一個 Annotation要想被子類繼承下來則使用 Inherited註釋說明
- 反射機制對於操作 Annotation是最重要的