1 Java反射機制概述
Reflection(反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。
1.1 Java反射機制提供的功能
Ø在運行時判斷任意一個對象所屬的類
Ø在運行時構造任意一個類的對象
Ø在運行時判斷任意一個類所具有的成員變量和方法
Ø在運行時調用任意一個對象的成員變量和方法
Ø生成動態代理
2.1 反射相關的主要API
Øjava.lang.Class:代表一個類
Øjava.lang.reflect.Method:代表類的方法
Øjava.lang.reflect.Field:代表類的成員變量
Øjava.lang.reflect.Constructor:代表類的構造方法
2 理解Class類並獲取Class類的實例
在Object類中定義了以下的方法,此方法將被所有子類繼承:
● public final Class getClass()
以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱。
2.1 Class類
對象照鏡子後可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪些接口。對於每個類而言,JRE 都爲其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個類的有關信息。
Class本身也是一個類
Class 對象只能由系統建立對象
一個類在 JVM 中只會有一個Class實例
一個Class對象對應的是一個加載到JVM中的一個.class文件
每個類的實例都會記得自己是由哪個 Class 實例所生成
通過Class可以完整地得到一個類中的完整結構
2.2 常用方法
方法名 |
功能說明 |
static Class forName(String name) |
返回指定類名 name 的 Class 對象 |
Object newInstance() |
調用缺省構造函數,返回該Class對象的一個實例 |
getName() |
返回此Class對象所表示的實體(類、接口、數組類、基本類型或void)名稱 |
Class getSuperClass() |
返回當前Class對象的父類的Class對象 |
Class [] getInterfaces() |
獲取當前Class對象的接口 |
ClassLoader getClassLoader() |
返回該類的類加載器 |
Class getSuperclass() |
返回表示此Class所表示的實體的超類的Class |
Constructor[] getConstructors() |
返回一個包含某些Constructor對象的數組 |
Field[] getDeclaredFields() |
返回Field對象的一個數組 |
Method getMethod(String name,Class … paramTypes) |
返回一個Method對象,此對象的形參類型爲paramType |
2.3 實例化Class類對象(四種方法)
1)前提:若已知具體的類,通過類的class屬性獲取,該方法
最爲安全可靠,程序性能最高
實例:Class clazz = String.class;
2)前提:已知某個類的實例,調用該實例的getClass()方法獲
取Class對象
實例:Class clazz = “www.yizhihanduxiu.com”.getClass();
3)前提:已知一個類的全類名,且該類在類路徑下,可通過
Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException
實例:Class clazz = Class.forName(“java.lang.String”);
4)其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“類的全類名”);
3 類的加載與ClassLoader的理解
3.1 類的加載過程
當程序主動使用某個類時,如果該類還未被加載到內存中,則系統會通過如下三個步驟來對該類進行初始化。
3.2 ClassLoader
類加載器是用來把類(class)裝載進內存的。JVM 規範定義了兩種類型的類加載器:啓動類加載器(bootstrap)和用戶自定義加載器(user-defined class loader)。 JVM在運行時會產生3個類加載器組成的初始化加載器層次結構 ,如下圖所示:
//1.獲取一個系統類加載器
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
//2.獲取系統類加載器的父類加載器,即擴展類加載器
classloader = classloader.getParent();
System.out.println(classloader);
//3.獲取擴展類加載器的父類加載器,即引導類加載器
classloader = classloader.getParent();
System.out.println(classloader);
//4.測試當前類由哪個類加載器進行加載
classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
//5.測試JDK提供的Object類由哪個類加載器加載
classloader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
//*6.關於類加載器的一個主要方法:getResourceAsStream(String str):獲取類路徑下的指定文件的輸入流
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);
4 通過反射創建運行時類的對象
創建類的對象:調用Class對象的newInstance()方法
要 求: 1)類必須有一個無參數的構造器。
2)類的構造器的訪問權限需要足夠。
難道沒有無參的構造器就不能創建對象了嗎?
不是!只要在操作的時候明確的調用類中的構造器,並將參數傳遞進去之後,纔可以實例化操作。步驟如下:
1)通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參類型的構造器
2)向構造器的形參中傳遞一個對象數組進去,裏面包含了構造器中所需的各個參數。
3)
//1.根據全類名獲取對應的Class對象
String name = “atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.調用指定參數結構的構造器,生成Constructor的實例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通過Constructor的實例創建對應類的對象,並初始化類屬性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);
5 通過反射獲取運行時類的完整結構
1.實現的全部接口
Øpublic Class<?>[] getInterfaces()
確定此對象所表示的類或接口實現的接口。
2.所繼承的父類
Øpublic Class<? Super T> getSuperclass()
返回表示此 Class 所表示的實體(類、接口、基本類型)的父類的 Class。
3.全部的構造器
Øpublic Constructor<T>[] getConstructors()
返回此 Class 對象所表示的類的所有public構造方法。
Øpublic Constructor<T>[] getDeclaredConstructors()
返回此 Class 對象表示的類聲明的所有構造方法。
lConstructor類中:
Ø取得修飾符: public int getModifiers();
Ø取得方法名稱: public String getName();
Ø取得參數的類型:public Class<?>[] getParameterTypes();
4.全部的方法
Øpublic Method[] getDeclaredMethods()
返回此Class對象所表示的類或接口的全部方法
Øpublic Method[] getMethods()
返回此Class對象所表示的類或接口的public的方法
lMethod類中:
Øpublic Class<?> getReturnType()取得全部的返回值
Øpublic Class<?>[] getParameterTypes()取得全部的參數
Øpublic int getModifiers()取得修飾符
Øpublic Class<?>[] getExceptionTypes()取得異常信息
5.全部的Field
Øpublic Field[] getFields()
返回此Class對象所表示的類或接口的public的Field。
Øpublic Field[] getDeclaredFields()
返回此Class對象所表示的類或接口的全部Field。
lField方法中:
Øpublic int getModifiers() 以整數形式返回此Field的修飾符
Øpublic Class<?> getType() 得到Field的屬性類型
Øpublic String getName() 返回Field的名稱。
6. Annotation相關
Øget Annotation(Class<T> annotationClass)
ØgetDeclaredAnnotations()
7.泛型相關
獲取父類泛型類型:Type getGenericSuperclass()
泛型類型:ParameterizedType
獲取實際的泛型類型參數數組:getActualTypeArguments()
8.類所在的包 Package getPackage()
6 通過反射調用運行時類的指定屬性、指定方法等
6.1 調用指定方法
通過反射,調用類中的方法,通過Method類完成。步驟:
1.通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型。
2.之後使用Object invoke(Object obj, Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息。
Object invoke(Object obj, Object … args)
說明:
1.Object 對應原方法的返回值,若原方法無返回值,此時返回null
2.若原方法若爲靜態方法,此時形參Object obj可爲null
3.若原方法形參列表爲空,則Object[] args爲null
4.若原方法聲明爲private,則需要在調用此invoke()方法前,顯式調用方法對象的setAccessible(true)方法,將可訪問private的方法。
6.2 調用指定屬性
在反射機制中,可以直接通過Field類操作類中的屬性,通過Field類提供的set()和get()方法就可以完成設置和取得屬性內容的操作。
Øpublic Field getField(String name) 返回此Class對象表示的類或接口的指定的public的Field。
Øpublic Field getDeclaredField(String name)返回此Class對象表示的類或接口的指定的Field。
在Field中:
Øpublic Object get(Object obj) 取得指定對象obj上此Field的屬性內容
Øpublic void set(Object obj,Object value) 設置指定對象obj上此Field的屬性內容
注:在類中屬性都設置爲private的前提下,在使用set()和get()方法時,首先要使用Field類中的setAccessible(true)方法將需要操作的屬性設置爲可以被外部訪問。
Øpublic void setAccessible(true)訪問私有屬性時,讓這個屬性可見。