Java基礎(chǔ)之反射詳解
前言
反射是我們框架的靈魂,反射也是我們框架的一個底層基石,沒有反射也就沒有框架,如果我們學(xué)好了反射,對我們閱讀框架底層是有很大班助的——阿俊。有些文章上來就講反射,就會很懵逼,不知道是干啥的,所以我們就引出一些問題來看看為什么需要反射
一、一個需求引出反射
看下面的問題
根據(jù)配置文件reflection.properties指定信息,創(chuàng)建People對象并調(diào)用方法hi
classullpath= com.reflection.People method=hi
思考:使用現(xiàn)有技術(shù),能做嗎?
我們可以來試一試
我們根據(jù)要求創(chuàng)建出兩個類和一個配置文件
People 類
public class People { private String name="zlj"; public void hi(){ System.out.println("hi"+name); } }
classullpath= com.reflection.People method=hi
在QuestionReflectionQuestion類中進行測試,我們學(xué)Java的時候可知,可以通過如下的方法創(chuàng)建People對象并調(diào)用方法hi
public class QuestionReflectionQuestion { public static void main(String[] args) { //傳統(tǒng)方式 People people = new People(); people.hi(); } }
那我們?nèi)绾瓮ㄟ^配置文件reflection.properties指定信息,創(chuàng)建People對象并調(diào)用方法hi呢?
注意:這樣的需求在學(xué)習(xí)框架時特別多,即通過外部文件配置,在不修改源碼情況下,來控制程序,也符合設(shè)計模式的ocp原則(開閉原則:不修改源碼來擴展功能)
嘗試1:Java中有一個Properties類,可以讀寫配置文件,我們可以用它進行嘗試
public class QuestionReflectionQuestion { public static void main(String[] args) throws IOException { //我們嘗試做一下-》明白反射 //Java中有一個Properties類,可以讀寫配置文件,我們可以用它進行嘗試 Properties properties = new Properties(); properties.load(new FileInputStream("src\\reflection.properties")); String classullpath = properties.get("classullpath").toString();//classullpath:com.reflection.People String method = properties.get("method").toString();//method:hi System.out.println("classullpath:"+classullpath); System.out.println("method:"+method); //那我們可以不可以直接使用new classullpath();來創(chuàng)建呢?這樣顯然不行的 } }
結(jié)論:嘗試失敗,我們就可以使用反射機制來解決上述:通過配置文件reflection.properties指定信息,創(chuàng)建People對象并調(diào)用方法hi,下一節(jié)進行代碼演示
二、反射入門
解決第一節(jié)問題的代碼演示
分四個步驟:
1.加載類,返回class類型對象:cls
2.通過cls得到你的加載類com.reflection.People的對象實例
3.通過cls得到你加載的類com.reflection.People的method=hi的方法對象(即:在反射中,可以把方法視為對象(即:萬物皆對象))
4.通過method調(diào)用方法:即通過方法對象來實現(xiàn)調(diào)用方法
public class QuestionReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Properties properties = new Properties(); properties.load(new FileInputStream("src\\reflection.properties")); String classullpath = properties.get("classullpath").toString();//classullpath:com.reflection.People String methodName = properties.get("method").toString();//method:hi System.out.println("====使用反射機制解決====="); //1:加載類,返回class類型對象:cls Class cls = Class.forName(classullpath); //2:通過cls得到你的加載類com.reflection.People的對象實例 Object o = cls.newInstance(); System.out.println("o的運行類型="+o.getClass());//運行類型 //3.通過cls得到你加載的類com.reflection.People的method=hi的方法對象 //即:在反射中,可以把方法視為對象(即:萬物皆對象) Method method = cls.getMethod(methodName); //4.通過method調(diào)用方法:即通過方法對象來實現(xiàn)調(diào)用方法 method.invoke(o);//傳統(tǒng)方法:對象.方法() 反射機制 方法.invoke(對象) } }
====使用反射機制解決===== o的運行類型=class com.reflection.People hizlj
我們先知道一下反射實現(xiàn)方式的步驟即可,一些類的介紹,我們后續(xù)會進行介紹
如果Java沒有反射機制,Java就不是一種動態(tài)語言,而且我們所說的spring mybatis 框架就都不存在了,反射機制是框架的靈魂,也是底層機制的基石
反射機制的牛之處就在于:可以通過外部文件配置,在不修改源碼的情況下,來控制程序(這就是開閉原則)
比如,我們在People類中新增cry方法,我們就不需要在QuestionReflectionQuestion類中新增 people.cry();這個方法,只需要在配置文件中reflection.properties進行如下修改method=cry即可。
====使用反射機制解決===== o的運行類型=class com.reflection.People cryzlj
三、反射原理圖
前面我們對反射進行了一個快速入門,接下來,我們來介紹一下它的原理
反射機制允許程序在執(zhí)行期借助于ReflectionAPI取得任何類的內(nèi)部信息(比如成員變量,構(gòu)造器,成員方法等等),并能操作對象的屬性及方法。反射在設(shè)計模式和框架底層都會用到
加載完類之后,在堆中就產(chǎn)生了一個Class類型的對象(一個類只有一個Class對象) ,這個對象包含了類的完整結(jié)構(gòu)信息。通過這個Class對象得到類的結(jié)構(gòu)。這個對象就像一面鏡子, 透過這個鏡子看到類的結(jié)構(gòu),所以,形象的稱之為反射。
我們可以拿第一節(jié)的例子,畫圖來說明
四、反射相關(guān)類
接下來講完反射機制原理,我們就來看一下反射機制可以做那些事情
1.在運行時判斷任意一個對象所屬的類
2.在運行時構(gòu)造任意一個類的對象
3.在運行時得到任意一個類所具有的成員變量和方法
4.在運行時調(diào)用任意一個對象的成員變量和方法
5.生成動態(tài)代理
反射相關(guān)的主要類:
1.java.lang.Class:代表個類, Class對象表示某 個類加載后在堆中的對象
2.java.lang.reflect.Method:代表類的方法
3.java.lang.reflect.Field: 代表類的成員變量
4.java.lang.reflect.Constructor:代表類的構(gòu)造方法
我們上面第二節(jié)介紹了Method演示,我們接下來進行Field和Constructor演示
People.java
public class People { public String name="zlj"; public People(){ this.name="zzg"; } public People(String name){ this.name=name; } public void hi(){ System.out.println("hi"+name); } public void cry(){ System.out.println("cry"+name); } }
QuestionReflectionQuestion.java
public class QuestionReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Properties properties = new Properties(); properties.load(new FileInputStream("src\\reflection.properties")); String classullpath = properties.get("classullpath").toString();//classullpath:com.reflection.People String methodName = properties.get("method").toString();//method:hi String fieldName = properties.get("field").toString(); String constructorName = properties.get("constructor").toString(); System.out.println("====使用反射機制解決====="); //1:加載類,返回class類型對象:cls Class cls = Class.forName(classullpath); //2:通過cls得到你的加載類com.reflection.People的對象實例 Object o = cls.newInstance(); System.out.println("o的運行類型="+o.getClass());//運行類型 //3.通過cls得到你加載的類com.reflection.Field 的field=name的字段 //即:在反射中,可以把方法視為對象(即:萬物皆對象) //getField不可以得到私有屬性 Field field = cls.getField(fieldName); //4.通過field 調(diào)用方法:即通過方法對象來實現(xiàn)調(diào)用方法 //因為對象賦了初始值;所以就是zzg,而不是zlj System.out.println(field.get(o)); System.out.println("People類的字段name是否為zlj:"+field.equals("zlj")); //構(gòu)造器的3,4和Field 一樣 Constructor constructors = cls.getConstructor();//()中可以指定構(gòu)造器參數(shù)類型。默認(rèn)返回?zé)o參構(gòu)造器 System.out.println(constructors); Constructor constructor = cls.getConstructor(String.class);//傳入構(gòu)造參數(shù)的類型對象 System.out.println(constructor); } }
reflection.properties
classullpath= com.reflection.People method=cry field=name constructor=People
演示
====使用反射機制解決===== o的運行類型=class com.reflection.People People類的字段name是否為zlj:false public com.reflection.People() public com.reflection.People(java.lang.String)
五、反射調(diào)用優(yōu)化
前面我們說了反射的原理和反射的作用,接下來我們就說一下它的優(yōu)缺點
反射優(yōu)點和缺點
優(yōu)點:可以動態(tài)的創(chuàng)建和使用對象(也是框架底層核心),使用靈活沒有反射機制,框架技術(shù)就失去底層支撐。
缺點:使用反射基本是解釋執(zhí)行,對執(zhí)行速度有影響。
基于缺點,我們可以進行反射調(diào)用優(yōu)化。
我們先看一下傳統(tǒng)方法調(diào)用和反射機制調(diào)用的耗時
public class ReflectionOptimize { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { ReflectionOptimize.m1(); ReflectionOptimize.m2(); } //傳統(tǒng)方法調(diào)用hi public static void m1(){ People people = new People(); long start = System.currentTimeMillis(); for (int i=0;i<900000;i++){ people.hi(); } long end = System.currentTimeMillis(); System.out.println("傳統(tǒng)方法調(diào)用hi耗時:"+(end-start)); } //反射機制調(diào)用hi public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<?> cls = Class.forName("com.reflection.People"); Object o = cls.newInstance(); Method method = cls.getMethod("hi"); 12 long start = System.currentTimeMillis(); for (int i=0;i<900000;i++){ method.invoke(o); } long end = System.currentTimeMillis(); System.out.println("反射機制調(diào)用hi耗時:"+(end-start)); } }
傳統(tǒng)方法調(diào)用hi耗時:4 反射機制調(diào)用hi耗時:19
結(jié)論:使用反射機制調(diào)用方法耗時更長
我們可以進行如下優(yōu)化
反射調(diào)用優(yōu)化關(guān)閉訪問檢查
1.Method和Field、 Constructor對象都有setAccessible()方法
2.setAccessible作用是啟動和禁用訪問安全檢查的開關(guān)
3.參數(shù)值為true表示 反射的對象在使用時取消訪問檢查,提高反射的效率。參數(shù)值為false則表示反射的對象執(zhí)行訪問檢查
代碼第12行加入method.setAccessible(true);//反射的對象在使用時取消訪問檢查,提高反射的效率
代碼效果演示
傳統(tǒng)方法調(diào)用hi耗時:3 反射機制調(diào)用hi耗時:16
到此這篇關(guān)于Java基礎(chǔ)之反射詳解的文章就介紹到這了,更多相關(guān)Java反射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Spring應(yīng)用中進行單元測試的解析和代碼演示
在Spring應(yīng)用中進行單元測試通常涉及到Spring TestContext Framework,它提供了豐富的注解和工具來支持單元測試和集成測試,以下是如何在Spring應(yīng)用中進行單元測試的詳細(xì)解析和代碼演示,需要的朋友可以參考下2024-06-06GsonFormat快速生成JSon實體類的實現(xiàn)
GsonFormat主要用于使用Gson庫將JSONObject格式的String?解析成實體,本文主要介紹了GsonFormat快速生成JSon實體類的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2023-05-05VSCode新手教程之配置Java環(huán)境的詳細(xì)教程
這篇文章主要給大家介紹了關(guān)于VSCode新手教程之配置Java環(huán)境的詳細(xì)教程,工欲善其事必先利其器,想要工作順利我們先搭建好JAVA的開發(fā)環(huán)境,需要的朋友可以參考下2023-10-10詳解Spring Boot實戰(zhàn)之Rest接口開發(fā)及數(shù)據(jù)庫基本操作
本篇文章主要介紹了Spring Boot實戰(zhàn)之Rest接口開發(fā)及數(shù)據(jù)庫基本操作,具有一定的參考價值,有興趣的可以了解一下2017-07-07Java利用數(shù)組隨機抽取幸運觀眾如何實現(xiàn)
這篇文章主要介紹了Java利用數(shù)組隨機抽取幸運觀眾如何實現(xiàn),需要的朋友可以參考下2014-02-02