JAVA:反射(參考書籍:《JAVA程式設計的邏輯》)
反射
類的載入
當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過 **載入,連線,初始化 **三步來實現對這個類進行初始化。
載入:就是指將class檔案讀入記憶體,併為之建立一個Class物件。任何類被使用時系統都會建立一個Class物件。
連線:驗證,是否有正確的內部結構,並和其他類協調一致。準備,負責為類的靜態成員分配記憶體,並設定預設初始化值。解析,將類的二進位制資料中的符號引用替換為直接引用。
初始化
類初始化時機
-
建立類的例項
-
類的靜態變數,或者為靜態變數賦值
-
類的靜態方法
-
使用反射方式來強制建立某個類或介面對應的java.lang.Class物件
-
初始化某個類的子類
-
直接使用java.exe命令來執行某個主類
反射
反射是在執行時,而非編譯時,動態獲取型別的資訊,比如介面資訊,成員資訊,方法資訊,構造方法資訊,再根據這些動態獲取到的資訊來建立物件,訪問、修改成員,使用方法。 ------- 《JAVA程式設計的邏輯》
Class類
Class沒有公共的構造方法。注意Class類不是class,大小寫要區分。這裡的前兩種獲取Class類物件的方法需要明確Person型別。而第三種需要指定這種型別的字串就行。
//通過Object類中的getClass()方法 , 返回Object類的執行時類
Person p = new Person();
Class c = p.getClass();
//通過 類名.class 獲取到位元組碼檔案物件
Class c2 = Person.class;
//將類名作為字串傳遞給Class類中的靜態方法forName
Class c3 = Class.forName("Person");
1、Class是個泛型類,有一個型別引數,getClass()並不知道具體的型別,只能返回 Class<?>。
2、介面也有Class物件。
3、**基本型別 **沒有getClass()方法,但都有對應的Class物件。void 作為特殊的返回型別,也有Class物件。
//基本型別。這裡就不列舉完了,還有Double,Character等
Class<Integer> inCls = int.class;
Class<Byte> byteCls = byte.class;
。。。。
//void型別
Class<void> voidCls = void.class;
4、對於陣列,每種型別的陣列都有對應的Class物件,每個 **維度 **都有一個。
獲取構造方法並使用
在反射機制中,把類中的成員(構造方法、成員方法、成員變數)都封裝成了對應的類進行表示。
其中,構造方法使用類 Constructor 表示。
//獲取public修飾, 指定引數型別所對應的構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//獲取指定引數型別所對應的構造方法(包含私有的)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//獲取所有的public 修飾的構造方法
public Constructor<?>[] getConstructors()
//獲取所有的構造方法(包含私有的)
public Constructor<?>[] getDeclaredConstructors()
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
//獲取Class物件
Class c = Class.forName("cn.itcast_01_Reflect.Person");//包名.類名
//獲取所有的構造方法
//這裡不止有一個構造方法,所以運用集合來儲存
Constructor[] cons = c.getDeclaredConstructors();
//用迴圈來列印方法
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("------------------------");
//獲取一個構造方法,這裡getConstructor的引數就是指定構造方法的引數。
//public Person()
Constructor con1 = c.getConstructor(null);
System.out.println(con1);
//public Person(String name)
Constructor con2 = c.getConstructor(String.class);
System.out.println(con2);
//private Person(String name, int age)
Constructor con3 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(con3);
//public Person(String name, int age, String address)
Constructor con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(con4);
}
}
獲取構造方法,建立物件
獲取 **構造方法 **,步驟如下:
- 獲取到Class物件。
- 獲取指定的構造方法。
- 通過構造方法類 Constructor 中的方法,建立物件。
獲取私有構造方法,建立物件
獲取 私有構造方法 ,步驟如下:
- 獲取到Class物件。
- 獲取指定的構造方法。
- 暴力訪問, 通過 setAccessible(boolean flag)方法,這個方法可以忽略JAVA的訪問許可權。
- 通過構造方法類 Constructor 中的方法,建立物件。
AccessibleObject 類是 Field、Method 和 Constructor 物件的 父類。它提供了將反射的物件標記為在使用時取消預設 Java 語言訪問控制檢查的能力,也就是可以打破JAVA的訪問許可權。
//引數值為 true 則指示反射的物件在使用時應該取消 Java 語言訪問檢查。引數值為 false 則指示反射的物件應該實施 Java 語言訪問檢查。
public void setAccessible(boolean flag) throws SecurityException
獲取成員變數並使用
在反射機制中,把類中的成員變數使用類 Field 表示。
//獲取指定的 public修飾的變數
public Field getField(String name)
//獲取指定的任意變數
public Field getDeclaredField(String name)
//獲取所有public 修飾的變數
public Field[] getFields()
//獲取所有的 變數 (包含私有)
public Field[] getDeclaredFields()
public class FieldDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
//獲取Class物件
Class c = Class.forName("cn.itcast_01_Reflect.Person");
//獲取成員變數
//多個變數,運用陣列來儲存
Field[] fields = c.getDeclaredFields();
//遍歷陣列,訪問陣列
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-----------------");
//一個變數
//public int age;
Field ageField = c.getField("age");
System.out.println(ageField);
//private String address,這裡是私有變數,也能被獲取。
Field addressField = c.getDeclaredField("address");
System.out.println(addressField);
}
}
成員變數進行賦值與獲取值操作
獲取成員變數,步驟如下:
-
獲取Class物件
-
獲取構造方法
-
通過構造方法,建立物件
-
獲取指定的成員變數(私有成員變數,通過setAccessible(boolean flag)方法暴力訪問)
//在指定物件obj中,將此 Field 物件表示的成員變數設定為指定的新值
public void set(Object obj, Object value)
//返回指定物件obj中,獲取物件的值
public Object get(Object obj)
//1,獲取Class物件
Class c = Class.forName("cn.itcast_01_Reflect.Person");
//2,獲取構造方法
//public Person(String name, int age, String address)
//這裡要傳入對應構造方法的引數型別的class物件!!!!!!!!!
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3,通過構造方法,建立物件
Object obj = con.newInstance("小明", 23, "哈爾濱");
//4,獲取指定的方法
//public void method1() 沒有返回值沒有引數的方法
//Method m1 = c.getMethod("method1", null);
//public String method4(String name)
Method m4 = c.getMethod("method4", String.class);
//5,執行找到的方法
//m1.invoke(obj, null);
Object result = m4.invoke(obj, "itcast");
獲取成員方法並使用
在反射機制中,把類中的成員方法使用類 Method 表示。
//獲取 public 修飾的方法
public Method getMethod(String name, Class<?>... parameterTypes)
//獲取任意的方法,包含私有的
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//引數1: name 要查詢的方法名稱; 引數2: parameterTypes 該方法的引數型別
//這裡因為返回不只一個方法,因此運用Method的陣列來存放。類似前面的。
//獲取本類與父類中所有public 修飾的方法
public Method[] getMethods()
//獲取本類中所有的方法(包含私有的)
public Method[] getDeclaredMethods()
建立物件呼叫指定的方法
//執行指定物件obj中,當前Method物件所代表的方法,方法要傳入的引數通過args指定
public Object invoke(Object obj, Object... args)
建立物件呼叫指定的 private 方法
獲取私有成員方法,步驟如下:
- 獲取Class物件
- 獲取構造方法
- 通過構造方法,建立物件
- 獲取指定的方法
- 開啟暴力訪問
- 執行找到的方法
也就是運用 setAccessible(true); 開啟暴力訪問。
總結
1、獲取.Class檔案物件的方法三種。
2、獲取構造方法。
3、獲取成員變數。
4、獲取成員方法。
5、暴力破除JAVA語言侷限,setAccessible (true)。