java中反射(Reflection)機(jī)制舉例詳解
一、什么是反射?
反射(Reflection)是一種 Java 程序運(yùn)行期間的動態(tài)技術(shù),可以在運(yùn)行時(shí)(runtime)檢査、修改其自身結(jié)構(gòu)或行為。通過反射,程序可以訪問、檢測和修改它自己的類、對象、方法、屬性等成員
二、反射的用途
- 動態(tài)加載類:程序可以在運(yùn)行時(shí)動態(tài)地加載類庫中的類;
- 動態(tài)創(chuàng)建對象:反射可以基于類的信息,程序可以在運(yùn)行時(shí),動態(tài)創(chuàng)建對象實(shí)例;
- 調(diào)用方法:反射可以根據(jù)方法名稱,程序可以在運(yùn)行時(shí),動態(tài)地調(diào)用對象的方法(即使方法在編寫程序時(shí)還沒有定義)
- 訪問成員變量:反射可以根據(jù)成員變量名稱,程序可以在運(yùn)行時(shí),訪問和修改成員變量(反射可以訪問私有成員變量)
- 運(yùn)行時(shí)類型信息:反射允許程序在運(yùn)行時(shí),查詢對象的類型信息,這對于編寫通用的代碼和庫非常有用;
Spring 框架使用反射來自動裝配組件,實(shí)現(xiàn)依賴注入;
MyBatis 框架使用反射來創(chuàng)建resultType 對象,封裝數(shù)據(jù)查詢結(jié)果;
三、獲取Class對象
反射的第一步是獲取 Class
對象。Class
對象表示某個(gè)類的元數(shù)據(jù),可以通過以下幾種方式獲?。?/p>
//獲取Class類型信息 public class Text02 { public static void main(String[] args) throws ClassNotFoundException { //方式1:通過類名 Class stringClass1 = String.class; //方式2:通過Class類的forName()方法 Class stringClass2 = Class.forName("java.lang.String"); //方式3:通過對象調(diào)用getClass()方法 Class stringClass3 = "".getClass(); System.out.println(stringClass1.hashCode());//1604839423 System.out.println(stringClass2.hashCode());//1604839423 System.out.println(stringClass3.hashCode());//1604839423 } }
四、Class類型的對象使用場景1
將一個(gè) JSON 字符串解析為 Java 對象,并輸出該對象的字段值。
//Class類型的對象使用場景1 public class Text03 { public static void main(String[] args) { String json= "{\"name\":\"長安荔枝\",\"favCount\":234}"; //方法定義 Document doc=JSON.parseObject(json,Document.class); System.out.println(doc.getName()); System.out.println(doc.getFavCount()); } }
使用 JSON.parseObject
方法將 JSON 字符串解析為 Document
類的對象。在解析過程中,JSON 字符串中的數(shù)據(jù)會自動映射到 Document
類的對應(yīng)字段中。
五、Class類型的對象使用場景2
通過 Class
對象在運(yùn)行時(shí)獲取一個(gè)類的相關(guān)信息,包括類名、包名、成員變量(字段)、成員方法等。
//Class類型的對象使用場景2 //獲取豐富的類型內(nèi)容 public class Text04 { public static void main(String[] args) throws ClassNotFoundException { Class clz = Class.forName("java.util.HashMap"); //獲取類名 System.out.println("完全限定名:"+clz.getName()); System.out.println("簡單的類名:"+clz.getSimpleName()); //獲取包名 System.out.println("package"+clz.getPackage().getName()); System.out.println(); //獲取成員變量 Field[] fieldArray =clz.getDeclaredFields(); System.out.println("成員變量(字段)"); for(Field field:fieldArray) { System.out.println(field); } System.out.println(); //獲取成員方法 Method[] methodArray = clz.getDeclaredMethods(); System.out.println("成員方法"); for(Method method:methodArray) { System.out.println(method); } } }
clz.getName()
返回類的完全限定名,包括包名,例如"java.util.HashMap"
。clz.getSimpleName()
返回類的簡單名稱,不包括包名,例如"HashMap"
。clz.getPackage().getName()
返回類所屬的包名,例如"java.util"
。clz.getDeclaredFields()
返回一個(gè)Field
數(shù)組,包含了類聲明的所有字段(包括私有字段)。clz.getDeclaredMethods()
返回一個(gè)Method
數(shù)組,包含了類聲明的所有方法(包括私有方法)。
六、通過反射創(chuàng)建對象
方式一:通過 Class
對象直接調(diào)用 newInstance()
方法
方式二:通過獲取構(gòu)造方法(Constructor
)來創(chuàng)建對象。
//通過反射的方式,創(chuàng)建對象 public class Text05 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { Class clz = Class.forName("com.apesource.demo01.Document"); //方式1:直接通過Class對象,調(diào)用newInstance()方法 Object objx = clz.newInstance();//相當(dāng)于在執(zhí)行無參構(gòu)造方法 //方式2:通過構(gòu)造器(構(gòu)造方法) //無參構(gòu)造方法 Constructor constructor1 = clz.getDeclaredConstructor();//獲取無參構(gòu)造方法 System.out.println(constructor1); Object obj1 = constructor1.newInstance();//執(zhí)行構(gòu)造器(構(gòu)造方法),創(chuàng)建對象 //有參構(gòu)造方法 Constructor constructor2 = clz.getDeclaredConstructor(String.class);//獲取有參構(gòu)造方法 System.out.println(constructor2); Object obj2 = constructor2.newInstance("兩京十五日"); Constructor constructor3 = clz.getDeclaredConstructor(int.class);//獲取有參構(gòu)造方法 System.out.println(constructor3); Object obj3 = constructor3.newInstance(34); Constructor constructor4 = clz.getDeclaredConstructor(String.class,int.class);//獲取有參構(gòu)造方法 System.out.println(constructor4); Object obj4 = constructor4.newInstance("風(fēng)起隴西",64); System.out.println(objx); System.out.println(obj1); System.out.println(obj2); System.out.println(obj3); System.out.println(obj4); }
newInstance()
方法是Class
對象提供的一個(gè)方法,它調(diào)用類的無參構(gòu)造方法來創(chuàng)建類的實(shí)例。- 注意:這個(gè)方法在 Java 9 以后已經(jīng)被棄用,推薦使用
Constructor
對象來創(chuàng)建實(shí)例。 - 通過
getDeclaredConstructor()
方法獲取Document
類的無參構(gòu)造方法,然后調(diào)用newInstance()
方法創(chuàng)建實(shí)例。 - 注意:如果類中沒有無參構(gòu)造方法,調(diào)用
getDeclaredConstructor()
會拋出NoSuchMethodException
。 - 通過
getDeclaredConstructor(String.class)
獲取帶有一個(gè)String
參數(shù)的構(gòu)造方法,并傳入"兩京十五日"
作為參數(shù)來創(chuàng)建對象。 - 通過
getDeclaredConstructor(int.class)
獲取帶有一個(gè)int
參數(shù)的構(gòu)造方法,并傳入34
作為參數(shù)來創(chuàng)建對象。
七、使用 Java 反射機(jī)制獲取和調(diào)用類的構(gòu)造方法,訪問私有構(gòu)造方法并創(chuàng)建對象
public class Text06 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class clz = Class.forName("com.apesource.demo01.Document"); //獲取一組構(gòu)造器 Constructor[] constructorArray1 = clz.getConstructors();//public Constructor[] constructorArray2 = clz.getDeclaredConstructors();//public、private //獲取指定構(gòu)造器 Constructor constructor1 = clz.getConstructor(); Constructor constructor2 = clz.getDeclaredConstructor(String.class); System.out.println(constructor1); System.out.println(constructor2); //調(diào)用私有構(gòu)造器,必須設(shè)置它的訪問全限 constructor2.setAccessible(true); //調(diào)用構(gòu)造器,創(chuàng)建對象 Object obj = constructor2.newInstance("長安三萬里"); System.out.println(obj); } }
getConstructors()
方法返回一個(gè)包含所有公共(public
)構(gòu)造方法的數(shù)組。如果類中沒有public
構(gòu)造方法,則返回空數(shù)組。getDeclaredConstructors()
方法返回一個(gè)包含所有聲明的構(gòu)造方法的數(shù)組(包括私有的、受保護(hù)的和默認(rèn)訪問級別的構(gòu)造方法)。getConstructor()
方法用于獲取類的無參構(gòu)造方法(必須是public
的)。如果沒有無參構(gòu)造方法或者不是public
,則拋出NoSuchMethodException
。getDeclaredConstructor(Class<?>... parameterTypes)
方法用于獲取指定參數(shù)類型的構(gòu)造方法。這里通過傳入String.class
參數(shù)獲取一個(gè)帶有String
參數(shù)的構(gòu)造方法。這個(gè)構(gòu)造方法可以是任何訪問級別(public
、private
、protected
、默認(rèn))。setAccessible(true)
用于繞過 Java 訪問控制機(jī)制,使私有構(gòu)造方法也可以被調(diào)用。如果不設(shè)置Accessible
為true
,那么調(diào)用私有構(gòu)造方法時(shí)會拋出IllegalAccessException
。newInstance(Object... initargs)
方法使用指定的構(gòu)造方法創(chuàng)建對象。這里調(diào)用了帶有String
參數(shù)的構(gòu)造方法,并傳入"長安三萬里"
作為參數(shù)。
八、通過反射,訪問并使用成員方法
public class Text08 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { //硬編碼的方式 // Document doc1 = new Document(); // doc1.setName("海底兩萬里"); // doc1.setFavCount(10025); //反射的方式 Class clz = Class.forName("com.apesource.demo01.Document");//獲取類型信息 Object doc1 = clz.newInstance();//創(chuàng)建對象 //獲取指定名稱和參數(shù)類型的方法 Method setNameMethod = clz.getMethod("setName", String.class); Method setFavCountMethod = clz.getMethod("setFavCount", int.class); //執(zhí)行方法 //doc1.setName("海底兩萬里"); setNameMethod.invoke(doc1, "海底兩萬里"); //doc1.setFavCount(10025); setFavCountMethod.invoke(doc1, 10025); System.out.println(doc1); } }
getMethod(String name, Class<?>... parameterTypes)
方法用于獲取類的某個(gè)public
方法。方法名稱和參數(shù)類型必須匹配才能成功獲取方法。invoke(Object obj, Object... args)
方法用于調(diào)用指定的實(shí)例方法。setNameMethod.invoke(doc1, "海底兩萬里");
等同于doc1.setName("海底兩萬里");
。setFavCountMethod.invoke(doc1, 10025);
等同于doc1.setFavCount(10025);
。
九、通過反射,調(diào)用靜態(tài)方法以及處理可變參數(shù)
public class Text09_01 { public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException { //硬編碼的方式 //調(diào)用靜態(tài)方法 // Document.dosth(); String ret1 = String.format("HashMap的默認(rèn)初始容量是%d,加載因子是%.2f", 16,0.75f); System.out.println(ret1); //反射的方式 // Class clz = Class.forName("com.apesource.demo01.Document"); // Method dosthMethod = clz.getMethod("dosth"); // dosthMethod.invoke(null); Class clz = String.class; Method formatMethod = clz.getMethod("format", String.class,Object[].class); String ret2 = formatMethod.invoke(null, "HashMap的默認(rèn)初始容量是%d,加載因子是%.2f",new Object[] {16,0.75f}).toString(); System.out.println(ret2); } }
- 這是直接調(diào)用靜態(tài)方法的傳統(tǒng)方式。假設(shè)
Document
類有一個(gè)名為dosth()
的靜態(tài)方法,可以直接使用類名調(diào)用。 String.format
是一個(gè)靜態(tài)方法,用于格式化字符串,類似于printf
的格式化規(guī)則- 獲取 String 類的 Class 對象:使用
Class clz = String.class;
。 - 獲取 String 類的 Class 對象:使用
Class clz = String.class;
。 - 調(diào)用靜態(tài)方法:使用
invoke(null, "HashMap的默認(rèn)初始容量是%d,加載因子是%.2f", new Object[] {16, 0.75f})
調(diào)用靜態(tài)方法,因?yàn)?nbsp;format
是靜態(tài)方法,所以第一個(gè)參數(shù)是null
。 - 注意
invoke
方法中,Object[]
參數(shù)必須以數(shù)組形式傳遞,所以用了new Object[] {16, 0.75f}
。
十、反射的性能問題
反射雖然功能強(qiáng)大,但由于是在運(yùn)行時(shí)動態(tài)操作類,因此性能相對較低。此外,反射也會破
壞封裝性,使用時(shí)要謹(jǐn)慎。
十一、反射的安全性
使用反射時(shí)需要注意安全問題,因?yàn)樗梢岳@過 Java 的訪問控制機(jī)制。例如,可以訪問私有
字段或方法,因此在開發(fā)中使用反射要特別小心。
十二、反射的常見場景
- 框架開發(fā):如 Spring 中的依賴注入、Hibernate 中的 ORM 等。
- 調(diào)試工具:如 Java 的調(diào)試器、分析工具等。
- 動態(tài)代理:在 Java 中,動態(tài)代理依賴于反射。
總結(jié)
到此這篇關(guān)于java中反射(Reflection)機(jī)制的文章就介紹到這了,更多相關(guān)java反射Reflection內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis如何通過接口查找對應(yīng)的mapper.xml及方法執(zhí)行詳解
這篇文章主要給大家介紹了利用mybatis如何通過接口查找對應(yīng)的mapper.xml及方法執(zhí)行的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-06-06SpringBoot使用AOP,內(nèi)部方法失效的解決方案
這篇文章主要介紹了SpringBoot使用AOP,內(nèi)部方法失效的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08springboot中nacos-client獲取配置的實(shí)現(xiàn)方法
本文主要介紹了springboot中nacos-client獲取配置的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Java中常用解析工具jackson及fastjson的使用
今天給大家?guī)淼氖顷P(guān)于Java解析工具的相關(guān)知識,文章圍繞著jackson及fastjson的使用展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Springboot和bootstrap實(shí)現(xiàn)shiro權(quán)限控制配置過程
這篇文章主要介紹了Springboot和bootstrap實(shí)現(xiàn)shiro權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Java編程實(shí)現(xiàn)打印螺旋矩陣實(shí)例代碼
這篇文章主要介紹了Java編程實(shí)現(xiàn)打印螺旋矩陣實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12