反射 框架設(shè)計(jì)的靈魂
Java中的反射顧名思義就是將類的各個(gè)組成部分封裝為其他對(duì)象鳄哭。使用這些封裝后的對(duì)象可以進(jìn)行一些操作攘乒。
對(duì)于反射機(jī)制义屏,我們可以說(shuō)反射就是框架設(shè)計(jì)的靈魂。很多框架內(nèi)部的機(jī)制都是反射溃蔫。
使用反射的好處就是:
- 可以在程序運(yùn)行過(guò)程中,操作這些對(duì)象
- 可以解耦琳猫,提高程序的可擴(kuò)展性
Class
說(shuō)起反射我們必須說(shuō)一個(gè)重要的類那就是Class伟叛。這個(gè)類代表了Java編譯后的字節(jié)碼對(duì)象。字節(jié)碼對(duì)象包含了定義類時(shí)所指定的全部的成員變量脐嫂、方法等屬性统刮。
Java執(zhí)行的三個(gè)階段
Java的執(zhí)行過(guò)程是:我將Java代碼的執(zhí)行階段分為3個(gè)階段紊遵。分別是Source源代碼階段、Class類對(duì)象階段侥蒙、Runtime運(yùn)行時(shí)階段
- Source源代碼階段:在這個(gè)階段中暗膜,Java會(huì)將源代碼編譯為字節(jié)碼文件,也就是我們平時(shí)見(jiàn)到的.class文件辉哥。
- Class類對(duì)象階段:在這個(gè)階段中桦山,當(dāng)我們使用對(duì)應(yīng)的對(duì)象時(shí),類加載器ClassLoader就會(huì)將字節(jié)碼文件對(duì)象加載進(jìn)內(nèi)存醋旦。Class對(duì)象中封裝了源代碼中成員變量恒水、構(gòu)造方法、成員方法饲齐。
- Runtime運(yùn)行時(shí)階段:在這個(gè)階段中在使用對(duì)應(yīng)的類對(duì)象的時(shí)候钉凌,JVM會(huì)將對(duì)應(yīng)的字節(jié)碼文件也就是我們前面說(shuō)過(guò)的Class對(duì)象加載進(jìn)內(nèi)存,這樣我們就能使用定義好的類和對(duì)象了捂人。
Class對(duì)象的方式
- Class.from("全類名") 將字節(jié)碼文件加載進(jìn)內(nèi)存御雕,返回一個(gè)class對(duì)象
- 一般多用于配置文件,將類名定義在配置文件中滥搭,讀取配置文件酸纲,加載類
- 類名.class 通過(guò)類名的屬性class獲取 class對(duì)象
- 多用于參數(shù)的傳遞
- 對(duì)象.getClass():getClass()方法定義在超類Object中
結(jié)論:同一個(gè)字節(jié)碼(*.class)在一次程序運(yùn)行過(guò)程中,只會(huì)被加載一次瑟匆,不論通過(guò)哪一種方式獲取的class對(duì)象都是同一個(gè)
Class對(duì)象功能
我們剛才說(shuō)了通過(guò)Class對(duì)象我們可以操作對(duì)象的屬性闽坡,那么我們?cè)趺传@取對(duì)象的屬性呢
獲取成員變量
- Field[] getFields():獲取所有public修飾的成員變量 返回一個(gè)Field數(shù)組
- Field getField(String name) 獲取指定名稱的public 修飾的變量
使用getFields只能獲取Public修飾的成員變量
- Field[] getDeclaredFields() 獲取所有的成員變量,不考慮修飾符
- Field getDeclaredField(String name) 獲取指定名稱的成員變量 不考慮修飾符
獲取構(gòu)造方法
- Constructor<?>[] getConstructors() 獲取構(gòu)造方法的對(duì)象數(shù)組 public修飾
- Constructor getConstructor(類<?>...parameterTypes)獲取指定參數(shù)的構(gòu)造方法 public修飾
- Constructor getDeclaredConstructor(類<?>...parameterTypes) 獲取構(gòu)造方法對(duì)象 不考慮修飾符
- Constructor[] getDeclaredConstructor() 獲取構(gòu)造方法的對(duì)象數(shù)組 不考慮修飾符
獲取成員方法
- Method[] getMethods() 獲取成員方法數(shù)組 public修飾
getMethods不僅僅會(huì)獲取類的成員方法愁溜,還會(huì)獲取父類的方法
- Method getMethod(String name,類<?>...parameterTypes) 獲取指定參數(shù)和類型的成員方法
- Method[] getDeclareMethods 獲取成員方法數(shù)組 不考慮修飾符
- Method getDeclareMethod(String name,類<?>....parameterTypes) 獲取指定參數(shù)的成員方法 不考慮修飾符
獲取全類名
- String getName() 獲取class對(duì)象的全類名
Field成員變量
我們獲取了Field成員變量后可以通過(guò)Field設(shè)置對(duì)應(yīng)的成員變量的值
1 設(shè)置值
* void set(Object obj,Object value)
2 獲取值
* get(Object obj)
3 忽略訪問(wèn)權(quán)限修飾符的安全檢查
* setAccessible(true) 暴力反射
Constructor 構(gòu)造方法
通過(guò)Constructor構(gòu)造方法對(duì)象疾嗅。
- T newInstance(Object...initargs) 創(chuàng)建對(duì)象
- 如果使用空參數(shù)構(gòu)造方法的創(chuàng)建對(duì)象,操作可以簡(jiǎn)化 Class對(duì)象的newInstance方法
Person person2 = (Person) cla.newInstance();
System.out.println(person2);
如果構(gòu)造器是private修飾的冕象,我們也可以調(diào)用construct的setAccessible來(lái)進(jìn)行暴力反射
constructor.setAccessible(true) //暴力反射
暴力反射的前提必須使用declare的方法
Method方法對(duì)象
- 執(zhí)行方法
- Object invoke(Object obj,Object...args)
- 獲取方法名稱
- String getName獲取方法名
普通的Method打印的是方法的全名(包名.類名.方法名)
而getName獲取的方法名就是方法的名稱
- String getName獲取方法名
學(xué)習(xí)了這么多代承,我們來(lái)實(shí)現(xiàn)一個(gè)案例來(lái)看一下反射的使用
案例
- 需求:寫一個(gè)小框架,在不改變?nèi)魏晤惖那闆r下渐扮,執(zhí)行類中的任意方法
- 步驟:
- 將需要?jiǎng)?chuàng)建的類的全類名和要執(zhí)行的方法定義在配置文件中
- 在程序中加載讀取配置文件
- 使用反射技術(shù)來(lái)加載類文件進(jìn)內(nèi)存
- 創(chuàng)建對(duì)象
- 執(zhí)行方法
- 定義一個(gè)配置文件 pro.properties
className=com.probuing.bean.Student
methodName=study
- 創(chuàng)建要執(zhí)行的實(shí)體類對(duì)象 Student.java
public class Student {
public void study() {
System.out.println("this is student is studing");
}
}
- 創(chuàng)建執(zhí)行框架
public class ReflectFrame {
public static void main(String[] args) {
try {
//加載配置文件
Properties pro = new Properties();
//獲取類加載器论悴,獲取配置文件路徑
ClassLoader classLoader = ReflectFrame.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
//加載配置文件,轉(zhuǎn)換為一個(gè)集合
pro.load(resourceAsStream);
//獲取配置文件中定義的數(shù)據(jù)
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//加載指定的類class進(jìn)內(nèi)存
Class<?> cla = Class.forName(className);
//創(chuàng)建對(duì)象
Student student = (Student) cla.newInstance();
//獲取方法對(duì)象
Method method = cla.getMethod(methodName);
//執(zhí)行方法
method.invoke(student);
} catch (Exception e) {
e.printStackTrace();
}
}
}