反射
1.反射機制
- 概念:
- 簡述:在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性==;這種動态擷取的資訊以及動态調用對象的方法的功能稱為java語言的反射機制。
- 詳述:Java反射是Java被視為動态(或準動态)語言的一個關鍵性質。這個機制允許程式在運作時透過ReflectionAPIs取得任何一個已知名稱的class的内部資訊,包括其modifiers(諸如public, static等)、superclass(例如Object),實作之interfaces(例如Cloneable),也包括fields和methods的所有資訊,并可于運作時改變fields内容或喚起methods。
- 類型資訊在jvm裡也是作為一個對象 隻保留一份
2.反射機制功能
- 在運作時判斷任意一個對象所屬的類;在運作時構造任意一個類的對象;在運作時判斷任意一個類所具有的成員變量和方法;在運作時調用任意一個對象的方法;生成動态代理。
3. Reflection API
- 當然JDK提供了反射API,我們可以學習裡面的方法來取得任何已知名稱的類的内部資訊。
Class對象的擷取
- 對象的getClass()方法;
- 類的.class(最安全/性能最好)屬性;
- 運用Class.forName(String className)動态加載類,className需要是類的全限定名(最常用).
對象建立
- 通過反射來生成對象的方式有兩種:
- 使用Class對象的newInstance() 方法來建立該Class對象對應類的執行個體(這種方式要求該Class對象的對應類有預設構造器).
- 先使用Class對象擷取指定的Constructor對象, 再調用Constructor對象的newInstance() 方法來建立該Class對象對應類的執行個體(通過這種方式可以選擇指定的構造器來建立執行個體).
從Class中擷取資訊
- 擷取類内資訊
内容 | 方法 | 說明 |
---|---|---|
構造器 | 類對象.getConstructors() | 擷取所有public修飾的構造方法, 傳回數組 |
構造器 | 類對象.getDeclareConstructors() | 擷取本類所有構造方法,傳回數組 |
構造器 | 類對象.getConstructor(參數) | 擷取指定的構造方法,傳回對象 |
構造器 | 類對象.getConstructor() | 擷取無參構造,傳回對象 |
包含的方法 | 類對象.getMethods() | 擷取所有public方法,包括繼承的 傳回數組 |
包含的方法 | 類對象.getDeclaredMethods() | 擷取本類的所有方法,傳回數組 |
包含的方法 | 類對象.getMethod(String name, Class<?>… parameterTypes) | 擷取指定的公共方法 ,傳回對象 |
包含的方法 | getDeclaredMethod(String name, Class<?>… parameterTypes) | 傳回一個Method對象,該對象反映此Class對象表示的類或接口的指定聲明方法 。 |
擷取屬性資訊 | 類對象.getFields() | 擷取所有的公共屬性 (包括繼承) |
擷取屬性資訊 | 類對象.getDeclareFields() | 擷取本類屬性 |
擷取屬性資訊 | 類對象.getField(屬性名) | 根據屬性名找屬性 公共的 包括繼承 |
擷取屬性資訊 | 類對象.getDeclaredField(屬性名) |
本類所有屬性
- 上面的僅是一些使用基礎API,具體的請參照Reflection API
4.調用方法
- 當擷取到某個類對應的Class對象之後, 就可以通過該Class對象的getMethod來擷取一個Method數組或Method對象.每個Method對象對應一個方法,在獲得Method對象之後,就可以通過調用invoke方法來調用該Method對象對應的方法.
- 缺點:調用複雜,效率底
- 優點:可以調用私有方法
5.setAccessible方法(性能)
- Method/Constructor/Field/Element都繼承了AccessibleObject,AccessibleObject類中有一個setAccessible方法
- 該方法有兩個作用:
- 啟用/禁用通路安全檢查開關:值為true,則訓示反射的對象在使用時取消Java語言通路檢查;值為false,則訓示應該實施Java語言的通路檢查;
- 可以禁止安全檢查, 提高反射的運作效率.
6.代碼–友善了解
代碼摘自大神部落格敬業的小碼哥
package fanshe;
/**
* 擷取Class對象的三種方式
* 1 Object ——> getClass();
* 2 任何資料類型(包括基本資料類型)都有一個“靜态”的class屬性
* 3 通過Class類的靜态方法:forName(String className)(常用)
*
*/
public class Fanshe {
public static void main(String[] args) {
//第一種方式擷取Class對象
Student stu1 = new Student();//這一new 産生一個Student對象,一個Class對象。
Class stuClass = stu1.getClass();//擷取Class對象
System.out.println(stuClass.getName());
//第二種方式擷取Class對象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判斷第一種方式擷取的Class對象和第二種方式擷取的是否是同一個
//第三種方式擷取Class對象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字元串必須是真實路徑,就是帶包名的類路徑,包名.類名
System.out.println(stuClass3 == stuClass2);//判斷三種方式是否擷取的是同一個Class對象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
```java
注意:在運作期間,一個類,隻有一個Class對象産生。
三種方式常用第三種,第一種對象都有了還要反射幹什麼。第二種需要導入類的包,依賴太強,不導包就抛編譯錯誤。一般都第三種,一個字元串可以傳入也可寫在配置檔案中等多種方法。
```java
package fanshe;
public class Student {
//---------------構造方法-------------------
//(預設的構造方法)
Student(String str){
System.out.println("(預設)的構造方法 s = " + str);
}
//無參構造方法
public Student(){
System.out.println("調用了公有、無參構造方法執行了。。。");
}
//有一個參數的構造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多個參數的構造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年齡:"+ age);//這的執行效率有問題,以後解決。
}
//受保護的構造方法
protected Student(boolean n){
System.out.println("受保護的構造方法 n = " + n);
}
//私有構造方法
private Student(int age){
System.out.println("私有的構造方法 年齡:"+ age);
}
}
package fanshe;
import java.lang.reflect.Constructor;
/*
* 通過Class對象可以擷取某個類中的:構造方法、成員變量、成員方法;并通路成員;
*
* 1.擷取構造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"構造方法
public Constructor[] getDeclaredConstructors():擷取所有的構造方法(包括私有、受保護、預設、公有)
* 2).擷取單個的方法,并調用:
* public Constructor getConstructor(Class... parameterTypes):擷取單個的"公有的"構造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):擷取"某個構造方法"可以是私有的,或受保護、預設、公有;
*
* 調用構造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class Constructors {
public static void main(String[] args) throws Exception {
//1.加載Class對象
Class clazz = Class.forName("fanshe.Student");
//2.擷取所有公有構造方法
System.out.println("**********************所有公有構造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的構造方法(包括:私有、受保護、預設、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************擷取公有、無參的構造方法*******************************");
Constructor con = clazz.getConstructor(null);
//1>、因為是無參的構造方法是以類型是一個null,不寫也可以:這裡需要的是一個參數的類型,切記是類型
//2>、傳回的是描述這個無參構造函數的類對象。
System.out.println("con = " + con);
//調用構造方法
Object obj = con.newInstance();
// System.out.println("obj = " + obj);
// Student stu = (Student)obj;
System.out.println("******************擷取私有構造方法,并調用*******************************");
con = clazz.getDeclaredConstructor(char.class);
System.out.println(con);
//調用構造方法
con.setAccessible(true);//暴力通路(忽略掉通路修飾符)
obj = con.newInstance('男');
}
}
- 輸出
**********************所有公有構造方法*********************************
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
************所有的構造方法(包括:私有、受保護、預設、公有)***************
private fanshe.Student(int)
protected fanshe.Student(boolean)
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
fanshe.Student(java.lang.String)
*****************擷取公有、無參的構造方法*******************************
con = public fanshe.Student()
調用了公有、無參構造方法執行了。。。
******************擷取私有構造方法,并調用*******************************
public fanshe.Student(char)
姓名:男