需求:
根據組態檔 re.properties 中指定的資訊,創建物件,并呼叫方法
classfullpath=com.hiibird.Cat
method=hi
使用現有技術能做到嗎?
//首先讀取組態檔
Properties properties = new Properties();
File file = new File("./Reflection/src/re.properties");
properties.load(new FileReader(file));
//可以獲得類名和包路徑,以及方法名,但現有方法無法利用這些資訊重構該類或者呼叫方法
String classFullPath = properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");
//new classFullPath(); //使用傳統方法無法利用String型別的報名創建類
//使用反射機制
//1. 加載類,回傳Class型別的物件
Class<?> clazz = Class.forName(classFullPath);
System.out.println(clazz);
//2. 通過aClass 的到加載的類 com.hiibird.Cat 的物件實體
Object o = clazz.getDeclaredConstructor().newInstance();
System.out.println("o的運行型別:" + o.getClass());//o的運行時型別還是com.hiibird.Cat
//3. 通過getMethod()方法得到加載的類 com.hiibird.Cat 的methodName "hi" 的方法物件
//即:在反射中,可以把方法視為物件(萬物皆物件)
Method method = clazz.getMethod(methodName);
//4. 呼叫:通過method 呼叫方法:即通過方法物件來實作呼叫方法
method.invoke(o); //傳統方法 物件.方法();反射機制:方法.invoke(物件)
這樣的需求在學習框架時特別多,即通過外部組態檔,在不修改原始碼的情況下來控制程式,也符合設計模式的ocp原則(開閉原則:不修改原始碼,擴容功能)
1. 反射機制
- 反射機制允許程式員在執行期借助于Reflection API取得任何類的內部資訊(比如成員變數,構造器和成員方法等),并能操作物件的屬性及方法,反射在設計模式和框架底層都會用到,
- 加載完類之后,在堆中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件包含了類的完整結構資訊,通過這個物件得到類的結構,這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以形象的稱之為反射,
1.1 Java反射機制的作用及優缺點
- 在運行時判斷任意一個物件所屬的類
- 在運行時構造任意一個類的物件
- 在運行時得到任意一個類所具有的成員變數和方法
- 在運行時呼叫任意一個物件的成員變數和方法
- 生成動態代理
反射機制的優缺點:優點:可以動態的創建和使用物件(也是框架底層核心),使用靈活,沒有反射機制,框架技術就失去底層支撐;缺點:使用反射基本是解釋執行,對執行速度有影響
1.2 反射相關的主要類
- java.lang.Class:代表一個類,Class物件表示某個類加載后在堆中的物件
Class<?> clazz = Class.forname(classfullpath);
Object instance = clazz.getDeclaredConstructor().newInstance();
- java.lang.reflect.Method:代表類的方法,Method物件表示某個類的方法
//傳統寫法:instance.method(),反射:method.invoke(instance);
Method method = clazz.getMethod(methodName);
method.invoke(instance);
- java.lang.reflect.Field:代表類的成員方法,Field物件標識某個類的成員變數,getField得不到私有的成員變數
//傳統寫法:instance.field,反射:field.get(instance)
Field field = clazz.getField(fieldName);
System.out.println(nameField.get(instance));
- java.lang.reflect.Constructor:代表類的構造方法,Constructor物件表示構造器
Constructor<?> constructor = clazz.getConstructor();//回傳無參構造器
//通過指定構造器引數型別,獲取有參構造器
Constructor constructor1 = clazz.getConstructor(String.class, Integer.class);
1.3 反射呼叫優化-關閉訪問檢查
- Method和Field、Constructor物件都有setAceessible()方法
- setAccessible作用是開啟和禁用訪問安全檢查的開關
- 引數值為true表示反射的物件在使用時取消訪問檢查,提高反射的效率,引數值為false則表示反射的物件執行訪問檢查
method.setAccessible(true);//在反射呼叫時取消訪問檢查,可以稍微提高性能
method.invoke(instance);
2. Class類
類定義:
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement
基本介紹:
- Class也是類,因此也繼承Object類
- Class類物件不是new出來的,其構造方法是私有的,只有JVM可以創建Class類物件
- 對于某個類的Class類物件,在記憶體中只有一份,因為類只加載一次,這個物件在類加載時由JVM創建
- 每個類的實體都會記得自己是由那個Class實體所生成
- 通過Class可以完成地得到一個類的完整結構,通過一系列API
- Class物件是存放在堆的
- 類的位元組碼二進制資料,是存放在方法區的,有的地方稱為類的元資料(包括方法代碼,變數名,方法名,訪問權限等等)
2.1 Class類的常用方法
方法名 | 功能說明 |
---|---|
static Class forName(String name) | 回傳指定類名name的Class物件 |
Object getConstructor().newInstance() | 呼叫預設建構式,回傳該Class物件的一個實體 |
getName() | 回傳此Class物件所表示的物體(類、介面、陣列類、基本型別等)名稱 |
class getSuperclass() | 回傳當前Class物件的超類的Class物件 |
Class[] getInterface() | 回傳當前Class物件的介面 |
ClassLoader getClassLoader() | 回傳該類的類加載器 |
Constructor[] getConstructors() | 回傳一個包含某些Constructor物件的陣列 |
Field[] getDeclaredFields() | 回傳Field物件的一個陣列 |
Method getMethod(String name, Class ... paramType) | 回傳一個Method物件,此物件的形參為paramType |
2.2 獲取Class物件的6中方式
- 前提:已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException,多用于組態檔,讀取類全路徑,加載類,實體:
Class clazz = Class.forName("java.lang.Cat");
- 前提:已知具體的類,通過類的class獲取,該方式最為安全可靠,程式性能最高,多用于引數傳遞,比如通過反射得到對應構造器物件,實體:
Class<Cat> clazz = Cat.class;
//用來傳遞引數
Constructor constructor = clazz.getConstructor(String.class, Integer.class);
- 前提:已知某個類的實體,呼叫該實體的getClass()方法獲取Class物件,多用于通過創建好的物件,獲取Class物件,實體:
Class<> clazz = instance.getClass();
- 其他方式:通過類加載器(有4種)獲取Class物件
ClassLoader cl = instance.getClass().getClassLoader();
Class clazz = cl.loadClass("classFullPath");
- 基本資料(int,char,boolean,float,double,byte,long,short)按如下方式得到Class物件:
Class<Integer> clazz = int.class;
- 基本資料型別對應的包裝類,可以通過.type得到Class物件:
Class<Integer> clazz = Integer.TYPE;
2.3 哪些型別有Class物件
- 外部類,成員內部類,靜態內部類,區域內部類,匿名內部類
- interface:介面
- 陣列
- enum:列舉
- annotation:注解
- 基本資料型別
- void
2.3 通過反射獲取類的結構資訊
java.lang.Class類
- getName:獲取全類名
- getSimpleName:獲取簡單類名
- getFields:獲取所有public修飾的屬性,包含本類以及父類的
- getDeclaredFields:獲取本類中所有屬性
- getMethods:獲取所有public修飾的方法,包含本類以及父類的
- getDeclaredMethods:獲取本類中所有的方法
- getConstructors:獲取本類所有public修飾的構造器,不包含父類的
- getDeclaredConstructors:獲取本類中所有的構造器
- getPackage:以Package的形式回傳包資訊
- getSuperClass:以Class形式回傳父類資訊
- getInterfaces:以Class形式回傳介面資訊
- getAnnotations:以Annotations[]形式回傳注解資訊
java.lang.reflect.Field類
- getModifiers:以interesting形式回傳修飾符【說明:默認修飾符是0,public是1,private是2,protected是4,static是8,final是16】:public static... -> 1+8 = 9
- getType:以Class形式回傳型別
- getName:回傳屬性名
java.lang.reflect.Method類
- getModifiers:以int形式回傳修飾符【說明:默認修飾符是0,public是1,private是2,protected是4,static是8,final是16】:public static... -> 1+8 = 9
- getReturnType:以Class形式獲取 回傳型別
- getName:回傳方法名
- getParameterTypes:以Class[]形式回傳引數型別陣列
java.lang.reflect.Constructor類
- getModifiers:以int形式回傳修飾符
- getName:回傳構造器名(全類名)
- getParameterType:以Class[]回傳引數型別資料
2.4. 通過反射創建物件
- 方法一:呼叫類中的public修飾的無參構造器
- 方法二:呼叫類中的指定構造器
- Class類相關方法:
- getDeclaredConstructor().newInstance():呼叫類中的無參構造器,獲取相應類的物件
- getConstructor(Class...clazz):根據引數串列,獲取對應的構造器物件
- getDeclaredConstructor(Class...clazz):根據引數串列,獲取對應的構造器物件
- Constructor類相關方法:
- setAccessible:暴破
- newInstance(Object...obj):呼叫構造器
2.5 通過反射訪問類中的成員
- 根據屬性名獲取Field物件:Field f = clazz.getDeclaredField(屬性名);
- 暴破: f.setAccessible(true); //f是Field物件
- 訪問 f.set(instance,value); f.get(instance);
- 注意如果是靜態屬性,則set和get中的引數o,可以寫成null
2.6 通過反射訪問類中的方法
- 根據方法名和引數串列獲取Method方法物件:
Method m = clazz.getDeclaredMethod(方法名, XX.class); - 獲取物件:Object o = clazz.getDeclaredConstructor().newInstance();
- 爆破:m.setAccessible(true);
- 訪問:Object returnValue = https://www.cnblogs.com/hiibird/archive/2023/04/17/m.invoke(o, 實參串列);
- 在反射中,如果方法有回傳值,統一回傳Object
- 注意:如果是靜態方法,則invoke的引數o,也可以寫成null
3 類加載
基本說明:反射機制是java實作動態語言的關鍵,也就是通過反射實作類動態加載,
- 靜態加載:編譯時加載相關的類,如果沒有則報錯,依賴性太強
- 動態加載:運行時加載需要的類,如果運行時不用該類,即使不存在該類,也不會報錯,降低了依賴性
3.1 類加載時機
- 當創建物件時(new)//靜態加載
- 當子類被加載時,父類也被加載//靜態加載
- 呼叫類的靜態成員時//靜態加載
- 通過反射//動態加載
3.2 類的加載程序
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550307.html
標籤:其他