反射
- 1、反射:架構的靈魂
-
- 1.1 概述
- 2、擷取Class對象
-
- 2.1 擷取Class對象方式
- 3、Class對象功能
-
- 3.1 擷取功能(部分,具體參考api文檔)
-
- 1、擷取成員變量
- 2、擷取構造方法
- 3、擷取成員方法
- 4、擷取全類名
- 3.2 java.lang.reflect.Field:成員變量
- 3.3 java.lang.reflect.Method:方法對象
- 4、反射機制的應用案例
-
- 需求
- 實作
- 思路
- 代碼實作
1、反射:架構的靈魂
1.1 概述
- 架構:半成品軟體。可以在架構的基礎上進行軟體開發,簡化編碼
反射:将類的各個組成部分封裝為其他對象,這就是反射機制;學習使用架構并不需要了解反射,但是如果想要自己寫一個架構,那麼就需要對反射機制有很深入的了解。
好處:
1. 可以在程式運作過程中,操作這些對象。
2. 可以解耦,提高程式的可擴充性。
在學習反射以前,我們先來了解一下Java代碼在計算機中所經曆的三個階段:

1、Source源代碼階段:.java被編譯成*.class位元組碼檔案。
2、Class類對象階段:.class位元組碼檔案被類加載器加載進記憶體,并将其封裝成Class對象(用于在記憶體中描述位元組碼檔案),Class對象将原位元組碼檔案中的成員變量抽取出來封裝成數組Field[],将原位元組碼檔案中的構造函數抽取出來封裝成數組Construction[],将成員方法封裝成數組Method[]。當然Class類内不止這三個,還封裝了很多,我們常用的就這三個。
3、RunTime運作時階段:使用new建立對象的過程。
2、擷取Class對象
2.1 擷取Class對象方式
Class.forName(“全類名”):将位元組碼檔案加載進記憶體,傳回Class對象
* 多用于配置檔案,将類名定義在配置檔案中。讀取檔案,加載類
2. 類名.class:通過類名的屬性class擷取
* 多用于參數的傳遞
3. 對象.getClass():getClass()方法在Object類中定義着。
* 多用于對象的擷取位元組碼的方式
* 結論:
同一個位元組碼檔案(*.class)在一次程式運作過程中,隻會被加載一次,不論通過哪一種方式擷取的Class對象都是同一個。
代碼實作:
//方法一:對象.getClass()
public void test01(){
Person p = new Person();
Class<? extends Person> aClass = p.getClass();
System.out.println(aClass);
}
//方法二:類名.class
public void test02(){
Class<Person> personClass = Person.class;
System.out.println(personClass);
}
test01和test02擷取類對象,都需要依賴api Person
//方法三:Class.forName("全類名");
public void test03() throws ClassNotFoundException {
//手動加載Person.class檔案進記憶體
Class aClass = Class.forName("net.zretc.reflect.Person");//傳入Person的路徑
System.out.println(aClass);
}
3、Class對象功能
3.1 擷取功能(部分,具體參考api文檔)
1、擷取成員變量
Field[] getFields() //擷取所有public修飾的成員變量
Field getField(String name) //擷取指定名稱的public修飾的成員變量
Field[] getDeclaredFields() //擷取所有的成員變量,不考慮修飾符
Field getDeclaredField(String name) //擷取指定的成員變量,不考慮修飾符
2、擷取構造方法
Constructor<?>[] getConstructors() //擷取所有public修飾的構造函數
Constructor<T> getConstructor(類<?>... parameterTypes) //擷取指定的public修飾的構造函數
Constructor<?>[] getDeclaredConstructors() //擷取所有的構造函數,不考慮修飾符
Constructor<T> getDeclaredConstructor(類<?>... parameterTypes) //擷取指定的構造函數,不考慮修飾符
3、擷取成員方法
Method[] getMethods() //擷取所有public修飾的成員方法
Method getMethod(String name, 類<?>... parameterTypes) //擷取指定名稱的public修飾的成員方法
Method[] getDeclaredMethods() //擷取所有的成員方法,不考慮修飾符
Method getDeclaredMethod(String name, 類<?>... parameterTypes) //擷取指定名稱的成員方法,不考慮修飾符
4、擷取全類名
String getName()
3.2 java.lang.reflect.Field:成員變量
- 操作:
- 設定值 void set(Object obj, Object value)
- 擷取值 Object get(Object obj)
- 忽略通路權限修飾符的安全檢查 setAccessible(true):暴力反射
Field的get/set方法舉例代碼:
測試實體類:
class SupClass{
public String gendar;
}
public class Person {
public String name;
byte[] b;
private int age;
public void show(){
System.out.println("show------null----");
}
public void show(String name,int age){
System.out.println("show------name---age----");
}
private void sup(){
System.out.println("sup---");
}
}
測試類:
public void test06() throws Exception {
Class pClass = Class.forName("net.zretc.reflect.Person");
Field name_field = pClass.getDeclaredField("name");//私有變量
Person person = (Person) pClass.newInstance();//執行個體化對象
//設定通路權限:暴力反射
name_field.setAccessible(true);
Object name_value = name_field.get(person);//person.getName()
System.out.println(name_value);
}
public void test05() throws Exception {
Class pClass = Class.forName("net.zretc.reflect.Person");
Field age_field = pClass.getField("age");
//執行個體化Person
Person person = (Person) pClass.newInstance();//newInstance相當于執行的空參數的構造器
//設定值
age_field.set(person,20); //person.setAge(20);
//檢視age變量的值
Object age_value = age_field.get(person); // person.getAge()
System.out.println(age_value);
}
3.3 java.lang.reflect.Method:方法對象
- 執行方法:Object invoke(Object obj, Object… args) 沒有傳回值,為null
-
擷取方法名稱:String getName:擷取方法名
代碼展示
public void test08() throws Exception {
//類對象
Class pClass = Class.forName("net.zretc.reflect.Person");
//執行個體化Person
Person person = (Person) pClass.newInstance();
Method test = pClass.getDeclaredMethod("test");//私有化方法
//暴力反射
test.setAccessible(true);
test.invoke(person);
//show方法對象
Method show = pClass.getMethod("show", String.class, int.class);
//執行show方法: person.show("hehe",30)
Object result = show.invoke(person, "hehe", 30);
System.out.println(result);//沒有傳回值,為null
}
4、反射機制的應用案例
需求
寫一個"架構",在不改變該類的任何代碼的前提下,可以幫我們建立任意類的對象,并且執行其中的任意方法。
實作
(1)配置檔案
(2)反射機制
思路
(1)将需要建立的對象的全類名和需要執行的方法定義在配置檔案中
(2)在程式中加載讀取配置檔案
(3)使用反射技術把類檔案加載進記憶體
(4)建立對象
(5)執行方法
代碼實作
實體類:
Person 類
public class Person {
//無參方法
public void eat(){
System.out.println("eat...");
}
}
Student類
public class Student {
//無參方法
public void study(){
System.out.println("I am a Student");
}
}
配置檔案編寫:
className =zretc.edu.cn.Person
methodName = eat
代碼:
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.加載配置檔案
//1.1建立Properties對象
Properties pro = new Properties();
//1.2加載配置檔案
//1.2.1擷取class目錄下的配置檔案(使用類加載器)
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
pro.load(inputStream);
//2.擷取配置檔案中定義的資料
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加載該類進記憶體
Class cls = Class.forName(className);
//4.建立對象
Object obj = cls.newInstance();
//5.擷取方法對象
Method method = cls.getMethod(methodName);
//6.執行方法
method.invoke(obj);
}
}