一、前期概要
1瘩欺、 什么是反射
Java 反射機(jī)制在程序運(yùn)行時(shí)必盖,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法俱饿;對(duì)于任意一個(gè)對(duì)象歌粥,都能夠調(diào)用它的任意一個(gè)方法和屬性。這種 動(dòng)態(tài)的獲取信息 以及 動(dòng)態(tài)調(diào)用對(duì)象的方法 的功能稱為 java 的反射機(jī)制拍埠。
反射中的反的理解:在使用的之前失驶,提前不知道需要使用什么類型的對(duì)象。只是在調(diào)用的時(shí)候枣购,才知道要調(diào)用的對(duì)象類型嬉探。這種反其道而行的就是反射中反的理解。
程序執(zhí)行分為編譯器和運(yùn)行期棉圈,編譯時(shí)刻加載一個(gè)類就稱為靜態(tài)加載類涩堤,運(yùn)行時(shí)刻加載類稱為動(dòng)態(tài)加載類,
核心思想 讓你在寫代碼的時(shí)候可以更加靈活分瘾,降低耦合定躏,提高代碼的自適應(yīng)能力。
反射框架提供如下常用的核心功能:
1.在運(yùn)行時(shí)判斷任意對(duì)象所屬的類芹敌;
2.在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象痊远;
3.在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法(通過(guò)反射甚至可以調(diào)用private方法);
4.在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法;
2氏捞、反射的主要用途
通用框架碧聪,很多框架都是配置化的(比如Spring通過(guò)xml配置Bean), 為了保證框架的通用性液茎,可能需要根據(jù)不同的配置文件加載不同的對(duì)象或者類逞姿,調(diào)用不同的方法辞嗡,這個(gè)時(shí)候就需要反射,運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象滞造。
3续室、缺點(diǎn)
- 性能不佳 - 由于java反射動(dòng)態(tài)解析類型,因此涉及掃描類路徑以查找要加載的類的處理谒养,從而導(dǎo)致性能降低挺狰。
- 安全限制 - Reflection需要運(yùn)行時(shí)權(quán)限,這些權(quán)限可能不適用于在安全管理器下運(yùn)行的系統(tǒng)买窟。由于安全管理器丰泊,這可能導(dǎo)致應(yīng)用程序在運(yùn)行時(shí)失敗。
- 安全問(wèn)題 - 使用反射始绍,我們可以訪問(wèn)我們不應(yīng)該訪問(wèn)的部分代碼瞳购,例如,我們可以訪問(wèn)類的私有字段并更改它的值亏推。這可能是嚴(yán)重的安全威脅学赛,并導(dǎo)致您的應(yīng)用程序出現(xiàn)異常行為。
- 高維護(hù) - 反射代碼很難理解和調(diào)試吞杭,在編譯時(shí)也無(wú)法找到代碼的任何問(wèn)題罢屈,因?yàn)檫@些類可能不可用,使其不太靈活且難以維護(hù)篇亭。
- Class 對(duì)象
- 類名
- 修飾符
- 包信息
- 父類
- 實(shí)現(xiàn)的接口
- 構(gòu)造器
- 方法
- 變量
- 注解
二缠捌、獲得 Class 對(duì)象
在運(yùn)行期間,一個(gè)類译蒂,只有一個(gè)Class對(duì)象產(chǎn)生
1曼月、類的靜態(tài)方法(常用) :
- 說(shuō)明
獲取指定的類完整的路徑相關(guān)聯(lián)類或接口的Class對(duì)象。 - 方法
// 掌握 public static Class<?> forName(String className) // 了解 public static Class<?> forName(String className, boolean initialize,ClassLoader loader)
- 區(qū)別
第一個(gè)默認(rèn)進(jìn)行初始化操作柔昼,
第二個(gè)可以指定是否進(jìn)行初始化操作哑芹。當(dāng)initialize=false
不進(jìn)行初始化操作,即不會(huì)執(zhí)行靜態(tài)代碼塊捕透。 - 舉個(gè)栗子
Class clazz = Class.forName("com.wener.reflect.Xxx") // 或者 Class clazz = Class.forName("com.wener.reflect.Xxx",initialize,this.getClass().getClassLoader)
2聪姿、使用 .class 靜態(tài)語(yǔ)法。
- 說(shuō)明
任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個(gè)“靜態(tài)”的class屬性 - 方法
Class<?> cls = 類型.class;
- 舉個(gè)栗子
Class<String> cls = String.class; System.out.println(cls.toString());
3、使用類對(duì)象的 getClass()
- 說(shuō)明
通過(guò)對(duì)象的實(shí)例來(lái)返回Class對(duì)象 - 方法
Class<?> cls = instance.getClass()
- 舉個(gè)栗子
public class User { } User user = new User(); Class<? extends User> clz = user.getClass();
4、總結(jié)
- 常用的是類的靜態(tài)方法,
- getClass()的話一般在繼承的時(shí)候用的比較多一點(diǎn),比如Android里的注解框架
- .class 靜態(tài)語(yǔ)法: 需要導(dǎo)入類的包稀蟋,依賴太強(qiáng)脉顿,不導(dǎo)包就拋編譯錯(cuò)誤
三腺办、創(chuàng)建實(shí)例
1.4、注意
- cls.newInstance()方法返回的是一個(gè)泛型T,我們要強(qiáng)轉(zhuǎn)成自定義類
- cls.newInstance()默認(rèn)返回的是類的無(wú)參數(shù)構(gòu)造對(duì)象
- 被反射機(jī)制加載的類必須有無(wú)參數(shù)構(gòu)造方法,否者運(yùn)行會(huì)拋出異常
四、屬性操作
1骆撇、說(shuō)明
類的成員變量也是一個(gè)對(duì)象擎场,它是java.lang.reflect.Field的一個(gè)對(duì)象羽德,所以我們通過(guò)java.lang.reflect.Field里面封裝的方法來(lái)獲取這些信息并且操作這些屬性
2、獲取單個(gè)成員字段
- 方法
// 通過(guò)字段名迅办,返回一個(gè)具體的具有public屬性的成員變量(包括父類的)
Field getField(String name)
// 通過(guò)字段名所有已聲明的所有成員變量(私有的 默認(rèn)的 共有的)宅静,但不能得到其父類的成員變量
Field getDeclaredField(String name)
- 舉個(gè)栗子
public class User { private String name; public String detail; } public static void main(String[] args) { try { Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); /** * 獲取類的指定名稱公開(kāi)的屬性 */ Field detail = cls.getField("detail"); System.out.println(detail); /** * 獲取類的指定名稱的的屬性(包括私有的屬性) */ Field name = cls.getDeclaredField("name"); System.out.println(name); } catch (ClassNotFoundException | NoSuchFieldException e) { e.printStackTrace(); } }
3、獲取所有成員字段
- 方法
// 獲取所有的”公有字段” Field[] getFields() // 獲取所有字段(私有站欺、受保護(hù)姨夹、默認(rèn)、公有) Field[] getDeclaredFields()
- 舉個(gè)栗子
public class TestReflectUserField { public static void main(String[] args) { try { Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); Field[] fields = cls.getFields(); for (Field field : fields) { System.out.println("類型: " + field.getType() + "方法名: " + field.getName()); } Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("類型: " + declaredField.getType() + "方法名: " + declaredField.getName()); } } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
4镊绪、字段賦值
- 方法
// 將指定對(duì)象參數(shù)上的此Field對(duì)象表示的字段設(shè)置為指定的新值 field.set(Object obj,Object value)
- 參數(shù)說(shuō)明
- Object obj: 字段所在的類的實(shí)例對(duì)象
- Object value : 新值
- 注意
// 如果要給私有變量賦值必須取消權(quán)限的訪問(wèn)控制 field.setAccessible(true);
- 舉個(gè)栗子
public static void main(String[] args) { try { // 1 實(shí)例化Class對(duì)象 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); /** * 獲取類的指定名稱公有的屬性 */ Field detail = cls.getField("detail"); System.out.println(detail); /** * 獲取類的所有的屬性(包括私有 默認(rèn)的 公有的) */ Field name = cls.getDeclaredField("name"); // 2.創(chuàng)建對(duì)象 Object o = cls.newInstance(); // 3 通過(guò)字段的set方法設(shè)置 name.set(o, "嬌嬌"); System.out.println(o.toString()); } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } }
五、方法操作
1洒忧、獲取單個(gè)方法
- 方法
// 方法返回一個(gè)特定的方法蝴韭,其中第一個(gè)參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)對(duì)應(yīng)Class的對(duì)象 public Method getMethod(String name, Class<?>... parameterTypes)
- 舉個(gè)栗子
public class User { private String name = "木木"; public String detail = "hello"; private int age; private BigDecimal balance; public void increment() { this.age++; System.out.println(age); } public BigDecimal getBalance() { return balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } private void say(int num) { System.out.println(num + "號(hào)技師"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", detail='" + detail + '\'' + '}'; } } public class TestReflectUser { public static void main(String[] args) { reflectMethod(); } public static void reflectMethod() { try { // 1.實(shí)例化class對(duì)象 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); // 2.實(shí)例化User對(duì)象 Object o = cls.newInstance(); // 3.獲取increment方法 Method method = cls.getMethod("increment"); // 有參數(shù)無(wú)返回值 Method setBalance = cls.getMethod("setBalance", BigDecimal.class); // 有返回值值無(wú)參數(shù) Method methodGet = cls.getMethod("getBalance"); // 獲取私有的方法 Method say = cls.getDeclaredMethod("say", int.class); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
2熙侍、獲取所有的方法
- 方法
// 返回類或接口聲明的所有方法榄鉴,包括公共、保護(hù)蛉抓、默認(rèn)(包)訪問(wèn)和私有方法庆尘,但不包括繼承的方法。 public Method[] getDeclaredMethods() throws SecurityException
// 返回某個(gè)類的所有公用(public)方法巷送,包括其繼承類的公用方法驶忌。 public Method[] getMethods() throws SecurityException
- 舉個(gè)栗子
public class TestReflectUser { public static void main(String[] args) { reflectMethod(); } public static void reflectMethod() { try { // 1.實(shí)例化class對(duì)象 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); // 2.實(shí)例化User對(duì)象 Object o = cls.newInstance(); // 獲取所有的共有的方法(包括父類的方法) Method[] methods = cls.getMethods(); for (Method method1 : methods) { System.out.println(method1.getName()); } // 獲取所有的方法(包括私有的,共有的,默認(rèn)的) Method[] declaredMethods = cls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod.getName()); } } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
3、調(diào)用方法
- 方法
Object invoke(Object obj, Object... args)
- 參數(shù)說(shuō)明
- obj - 從中調(diào)用底層方法的對(duì)象笑跛,必須是實(shí)例化的對(duì)象
- args - 用于方法調(diào)用的參數(shù)付魔,是個(gè)Object數(shù)組,因?yàn)閰?shù)有可能有多個(gè)
- 返回值
使用參數(shù) args 在 obj 上指派該對(duì)象所表示方法的結(jié)果 - 舉個(gè)栗子
public class User { private String name = "木木"; public String detail = "hello"; private int age; private BigDecimal balance; public void increment() { this.age++; System.out.println(age); } public BigDecimal getBalance() { return balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } private void say(int num) { System.out.println(num + "號(hào)技師"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", detail='" + detail + '\'' + '}'; } } public class TestReflectUser { public static void main(String[] args) { reflectMethod(); } public static void reflectMethod() { try { // 1.實(shí)例化class對(duì)象 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); // 2.實(shí)例化User對(duì)象 Object o = cls.newInstance(); // 3.獲取set方法 Method method = cls.getMethod("increment"); // 4 執(zhí)行方法 method.invoke(o); // 有參數(shù)無(wú)返回值 Method setBalance = cls.getMethod("setBalance", BigDecimal.class); Object methodSet = setBalance.invoke(o, new BigDecimal(100.00)); System.out.println(methodSet); // 有返回值值無(wú)參數(shù) Method methodGet = cls.getMethod("getBalance"); Object invoke = methodGet.invoke(o); System.out.println(invoke); // 獲取私有的方法 Method say = cls.getDeclaredMethod("say", int.class); // 運(yùn)行時(shí)取消訪問(wèn)權(quán)限檢測(cè)機(jī)制 say.setAccessible(true); // 執(zhí)行方法 say.invoke(o, 1); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
4飞蹂、其它API
返回值 方法 | 說(shuō)明 |
---|---|
String getName() | 獲取方法的名稱 |
int getModifiers() | 獲取方法的修飾符 |
Class<?> getReturnType() Type getGenericReturnType
|
返回方法的返回值類型 |
Class<?>[] getParameterTypes() Type[] getGenericParameterTypes() | 返回方法的參數(shù)(列表) |
Class<?>[] getExceptionTypes() Type[] getGenericExceptionTypes() | 返回方法的異常信息 |
六几苍、綜合案例
1、通過(guò)配置文件動(dòng)態(tài)切換
- 說(shuō)明
反射非常強(qiáng)大陈哑,但是學(xué)習(xí)了之后妻坝,會(huì)不知道該如何使用,反而覺(jué)得還不如直接調(diào)用方法來(lái)的直接和方便惊窖。
但等我們后面接觸到一些框架之后才會(huì)有一些感觸 - 測(cè)試類
package com.wener.reflect.demo2; public class ReflectDemo1 { public void say() { System.out.println("ReflectDemo1"); } } public class ReflectDemo2 { public void say() { System.out.println("ReflectDemo2"); } }
- 配置文件reflect.properties
class=ReflectDemo2. method=say
- 測(cè)試代碼
public static void main(String[] args) { //從spring.txt中獲取類名稱和方法名稱 File springConfigFile = new File("/Users/zhangwei/work/IdeaProjects/JavaExample/ReflectExample/src/reflect.properties"); Properties properties = new Properties(); try { properties.load(new FileInputStream(springConfigFile)); String className = (String) properties.get("class"); String methodName = (String) properties.get("method"); //根據(jù)類名稱獲取類對(duì)象 Class cls = Class.forName(className); //根據(jù)方法名稱刽宪,獲取方法對(duì)象 Method m = cls.getMethod(methodName); //根據(jù)構(gòu)造器,實(shí)例化出對(duì)象 Object service = cls.newInstance(); //調(diào)用對(duì)象的指定方法 m.invoke(service); } catch (IOException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException | InstantiationException e) { e.printStackTrace(); } }
- 優(yōu)點(diǎn)
當(dāng)需要從調(diào)用第一個(gè)類的方法界酒,切換到調(diào)用第二類的方法的時(shí)候纠屋,不需要修改一行代碼