<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          談?wù)凧ava反射:從入門到實踐,再到原理

          共 7317字,需瀏覽 15分鐘

           ·

          2021-04-25 21:53

          走過路過不要錯過

          點擊藍(lán)字關(guān)注我們


          前言

          反射是Java底層框架的靈魂技術(shù),學(xué)習(xí)反射非常有必要,本文將從入門概念,到實踐,再到原理講解反射,希望對大家有幫助。

          反射理解

          官方解析

          Oracle 官方對反射的解釋是:

          Reflection is commonly used by programs which require the ability to examine ormodify the runtime behavior of applications running in the Java virtual machine.This is a relatively advanced feature and should be used only by developers whohave a strong grasp of the fundamentals of the language. With that caveat inmind, reflection is a powerful technique and can enable applications to performoperations which would otherwise be impossible.

          Java 的反射機制是指在運行狀態(tài)中,對于任意一個類都能夠知道這個類所有的屬性和方法;并且對于任意一個對象,都能夠調(diào)用它的任意一個方法;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能成為Java語言的反射機制。


          白話理解


          正射


          萬物有陰必有陽,有正必有反。既然有反射,就必有“正射”。


          那么正射是什么呢?


          我們在編寫代碼時,當(dāng)需要使用到某一個類的時候,都會先了解這個類是做什么的。然后實例化這個類,接著用實例化好的對象進(jìn)行操作,這就是正射。

          Student student = new Student();student.doHomework("數(shù)學(xué)");

          反射

          反射就是,一開始并不知道我們要初始化的類對象是什么,自然也無法使用 new 關(guān)鍵字來創(chuàng)建對象了。

           Class clazz = Class.forName("reflection.Student"); Method method = clazz.getMethod("doHomework", String.class); Constructor constructor = clazz.getConstructor(); Object object = constructor.newInstance(); method.invoke(object, "語文");

          正射與反射對比

          以上兩段代碼,執(zhí)行效果是一樣的,如圖





          但是,其實現(xiàn)的過程還是有很大的差別的:

          • 第一段代碼在未運行前就已經(jīng)知道了要運行的類是Student

          • 第二段代碼則是到整個程序運行的時候,從字符串reflection.Student,才知道要操作的類是Student


          結(jié)論


          反射就是在運行時才知道要操作的類是什么,并且可以在運行時獲取類的完整構(gòu)造,并調(diào)用對應(yīng)的方法。


          Class 對象理解

          要理解Class對象,我們先來了解一下RTTI吧。RTTI(Run-Time Type Identification)運行時類型識別,其作用是在運行時識別一個對象的類型和類的信息。


          Java是如何讓我們在運行時識別對象和類的信息的?主要有兩種方式:一種是傳統(tǒng)的RRTI,它假定我們在編譯期已知道了所有類型。另一種是反射機制,它允許我們在運行時發(fā)現(xiàn)和使用類的信息。


          每個類都有一個Class對象,每當(dāng)編譯一個新類就產(chǎn)生一個Class對象(更恰當(dāng)?shù)卣f,是被保存在一個同名的.class文件中)。比如創(chuàng)建一個Student類,那么,JVM就會創(chuàng)建一個Student對應(yīng)Class類的Class對象,該Class對象保存了Student類相關(guān)的類型信息。





          Class類的對象作用是運行時提供或獲得某個對象的類型信息


          反射的基本使用


          獲取 Class 類對象


          獲取反射中的Class對象有三種方法。

          第一種,使用 Class.forName 靜態(tài)方法。

          Class class1 = Class.forName("reflection.TestReflection");

          第二種,使用類的.class 方法

          Class class2 = TestReflection.class;

          第三種,使用實例對象的 getClass() 方法。

          TestReflection testReflection = new TestReflection();Class class3 = testReflection.getClass();

          反射創(chuàng)造對象,獲取方法,成員變量,構(gòu)造器

          本小節(jié)學(xué)習(xí)反射的基本API用法,如獲取方法,成員變量等。

          反射創(chuàng)造對象

          通過反射創(chuàng)建類對象主要有兩種方式:

          實例代碼:

          //方式一Class class1 = Class.forName("reflection.Student");Student student = (Student) class1.newInstance();System.out.println(student);
          //方式二Constructor constructor = class1.getConstructor();Student student1 = (Student) constructor.newInstance();System.out.println(student1);

          運行結(jié)果:

          反射獲取類的構(gòu)造器

          看一個例子吧:

          Class class1 = Class.forName("reflection.Student");Constructor[] constructors = class1.getDeclaredConstructors();for (int i = 0; i < constructors.length; i++) {    System.out.println(constructors[i]); }

          反射獲取類的成員變量

          看demo:

          // student 一個私有屬性age,一個公有屬性emailpublic class Student {
          private Integer age;
          public String email;}
          public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class class1 = Class.forName("reflection.Student"); Field email = class1.getField("email"); System.out.println(email); Field age = class1.getField("age"); System.out.println(age); }}

          運行結(jié)果:

          getField(String name) 根據(jù)參數(shù)變量名,返回一個具體的具有public屬性的成員變量,如果該變量不是public屬性,則報異常。

          反射獲取類的方法

          demo

          public class Student {
          private void testPrivateMethod() { } public void testPublicMethod() { }}
          public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class class1 = Class.forName("reflection.Student");
          Method[] methods = class1.getMethods(); for (int i = 0; i < methods.length; i++) { System.out.println(methods[i]); } }}

          運行結(jié)果:

          反射的實現(xiàn)原理

          通過上一小節(jié)學(xué)習(xí),我們已經(jīng)知道反射的基本API用法了。接下來,跟著一個例子,學(xué)習(xí)反射方法的執(zhí)行鏈路。

          public class TestReflection {    public static void main(String[] args) throws Exception {        Class clazz = Class.forName("reflection.TestReflection");        Method method = clazz.getMethod("target", String.class);        method.invoke(null, "666");    }
          public static void target(String str) { //打印堆棧信息 new Exception("#" +str).printStackTrace(); System.out.println("invoke target method"); }}

          堆棧信息反映出反射調(diào)用鏈路:

          java.lang.Exception: #666invoke target method  at reflection.TestReflection.target(TestReflection.java:17)  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  at java.lang.reflect.Method.invoke(Method.java:498)  at reflection.TestReflection.main(TestReflection.java:11)

          invoke方法執(zhí)行時序圖

          我們跟著反射鏈路去看一下源碼,先看Method的invoke方法:

          public Object invoke(Object obj, Object... args)    throws IllegalAccessException, IllegalArgumentException,       InvocationTargetException{    //校驗權(quán)限    if (!override) {        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {            Class<?> caller = Reflection.getCallerClass();            checkAccess(caller, clazz, obj, modifiers);        }    }    MethodAccessor ma = methodAccessor;             // read volatile    if (ma == null) {        ma = acquireMethodAccessor(); //獲取MethodAccessor    }    //返回MethodAccessor.invoke    return ma.invoke(obj, args);}

          由上可知道,Method 的 invoke 方法,其實是返回接口MethodAccessor的invoke方法。MethodAccessor接口有三個實現(xiàn)類,到底調(diào)用的是哪個類的 invoke 方法呢?




          進(jìn)入acquireMethodAccessor方法,可以看到MethodAccessor由ReflectionFactory 的 newMethodAccessor方法決定。



          再進(jìn)ReflectionFactory的newMethodAccessor方法,我們可以看到返回的是DelegatingMethodAccessorImpl對象,也就是說調(diào)用的是它的invoke方法。




          再看DelegatingMethodAccessorImpl的invoke方法



          DelegatingMethodAccessorImpl的invoke方法返回的是MethodAccessorImpl的invoke方法,而MethodAccessorImpl的invoke方法,由它的子類NativeMethodAccessorImpl重寫,這時候返回的是本地方法invoke0,如下



          因此,Method的invoke方法,是由本地方法invoke0決定的,再底層就是c++相關(guān)了,有興趣的朋友可以繼續(xù)往下研究。


          反射的一些應(yīng)用以及問題


          反射應(yīng)用


          反射是Java框架的靈魂技術(shù),很多框架都使用了反射技術(shù),如spring,Mybatis,Hibernate等。


          JDBC 的數(shù)據(jù)庫的連接


          在JDBC連接數(shù)據(jù)庫中,一般包括加載驅(qū)動,獲得數(shù)據(jù)庫連接等步驟。而加載驅(qū)動,就是引入相關(guān)Jar包后,通過Class.forName() 即反射技術(shù),加載數(shù)據(jù)庫的驅(qū)動程序。


          Spring 框架的使用


          Spring 通過 XML 配置模式裝載 Bean,也是反射的一個典型例子。

          裝載過程:

          • 將程序內(nèi)XML 配置文件加載入內(nèi)存中

          • Java類解析xml里面的內(nèi)容,得到相關(guān)字節(jié)碼信息

          • 使用反射機制,得到Class實例

          • 動態(tài)配置實例的屬性,使用


          這樣做當(dāng)然是有好處的:


          不用每次都去new實例了,并且可以修改配置文件,比較靈活。


          反射存在的問題


          性能問題


          java反射的性能并不好,原主要是編譯器沒法對反射相關(guān)的代碼做優(yōu)化。

          安全問題

          我們知道單例模式的設(shè)計過程中,會強調(diào)將構(gòu)造器設(shè)計為私有,因為這樣可以防止從外部構(gòu)造對象。但是反射可以獲取類中的域、方法、構(gòu)造器,修改訪問權(quán)限。所以這樣并不一定是安全的。

          看個例子吧,通過反射使用私有構(gòu)造器實例化。

          public class Student {    private String name;    private Student(String name) {        System.out.println("我是私有構(gòu)造器,我被實例化了");        this.name = name;    }    public void doHomework(String subject) {        System.out.println("我的名字是" + name);        System.out.println("我在做"+subject+"作業(yè)");    }}public class TestReflection {    public static void main(String[] args) throws Exception {        Class clazz = Class.forName("reflection.Student");        // 獲取私有構(gòu)造方法對象        Constructor constructor = clazz.getDeclaredConstructor(String.class);        // true指示反射的對象在使用時應(yīng)該取消Java語言訪問檢查。        constructor.setAccessible(true);        Student student = (Student) constructor.newInstance("jay@huaxiao");        student.doHomework("數(shù)學(xué)");    }}

          運行結(jié)果:

          顯然,反射不管你是不是私有,一樣可以調(diào)用。所以,使用反射通常需要程序的運行沒有安全限制。如果一個程序?qū)Π踩杂袕娭埔螅詈貌灰褂梅瓷淅病?/span>





          往期精彩推薦



          騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

          面試:史上最全多線程面試題 !

          最新阿里內(nèi)推Java后端面試題

          JVM難學(xué)?那是因為你沒認(rèn)真看完這篇文章


          END


          關(guān)注作者微信公眾號 —《JAVA爛豬皮》


          了解更多java后端架構(gòu)知識以及最新面試寶典


          你點的每個好看,我都認(rèn)真當(dāng)成了


          看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲成人社区网站 | 欧美乱伦激情 | 日韩精品免费无码中文字幕 | 91亚洲精品久久久久久久久久久久 | 欧美性猛交 |