爲什麼需要Java反射?

目錄

 

什麼是反射?

反射的實現原理

反射的使用

爲什麼需要反射?(反射的作用/應用場景)

反射的缺點


什麼是反射?

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

 

反射的實現原理

java類加載:java類加載就是類加載器根據類的全限定名把.class的二進制字節碼代表的靜態存儲結構轉化爲方法區的運行時數據結構,然後在內存中生成代表該類的Class對象,一個類有且只有一個Class對象。每次生成Java對象實際上都是通過這個Class對象獲取整個類的結構並生成相應的java對象。(接口和抽象類也會被加載爲class對象)

所以如果能夠在運行時拿到Class對象,就可以生成java對象並進行調用,這就是java反射的本質。

 

反射的使用

Java 中的 java.lang.reflect 包提供了反射功能。java.lang.reflect 包中的類都沒有 public 構造方法。

/*
        * 獲取Class對象的3種方法:
        1、Class.forName("pacakge.className")
        2、Class.class
        3、javaObject.getClass()
        * */
        clazz = Class.forName("com.meituan.data.springbootdemo.User");
//        clazz = user.getClass();
//        clazz = User.class;
        System.out.println(clazz);

        /*
        * 判斷是否爲某個類的實例的2種方法:
        1、instanceof 關鍵字
        2、Class 對象的 isInstance 方法(它是一個 Native 方法)
        * */
        Boolean isInstance;
//        isInstance = user instanceof User;
        isInstance = clazz.isInstance(user);
        System.out.println("user is instance of User: "+isInstance);

        /*
        * 通過反射來創建實例對象的2種方法:
        1、Class 對象的 newInstance 方法。
        2、Constructor 對象的 newInstance 方法。
        * */
        User userNew;
//        userNew = (User) clazz.newInstance();
        Constructor constructor = clazz.getConstructor(String.class,Integer.class,String.class);
        userNew = (User) constructor.newInstance("ck",88,"234567");
        System.out.println(userNew);

        /*
        * 獲取代理類的方法Method,並調用該方法(invoke)
        * */
        Method method = clazz.getMethod("getAge",null);
        System.out.println(method.invoke(user));
        
        /*
        * 獲取代理類的構造器Constructor
        * */
        
        /*
         * 獲取代理類的註解Annotation,並根據不同的註解進行不同的行爲
         * */
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation:annotations){
            System.out.println(annotation);
            if(annotation instanceof MyPrintAnnotation){
                System.out.println(user);
            }
            if(annotation instanceof MyAnnotation){
                System.out.println(((MyAnnotation) annotation).msg());
            }
        }

 

爲什麼需要反射?(反射的作用/應用場景)

反射的作用可以用一句話概括:反射賦予了jvm動態編譯的能力。動態編譯可以最大限度的體現Java的靈活性(多態)。

否則類的元信息只能通過靜態編譯的形式實現(在編譯期確定類型,綁定對象),而不能實現動態編譯(在運行期確定類型,綁定對象)。也就是說在編譯以後,程序在運行時的行爲就是固定的了,如果要在運行時改變程序的行爲,就需要動態編譯,在Java中就需要反射機制。

 

情景一:不得已而爲之

有的類是我們在編寫程序的時候無法使用new一個對象來實例化對象的。例如:

  • 調用的是來自網絡的二進制.class文件,而沒有其.java代碼;
  • 註解 - 註解本身僅僅是起到標記作用,它需要利用反射機制,根據註解標記去調用註解解釋器,執行行爲。如果沒有反射機制,註解並不比註釋更有用。

 

情景二:動態加載(可以最大限度的體現Java的靈活性,並降低類的耦合性:多態)

有的類可以在用到時再動態加載到jvm中,這樣可以減少jvm的啓動時間,同時更重要的是可以動態的加載需要的對象(多態)。例如:

  • 動態代理 - 在切面編程(AOP)中,需要攔截特定的方法,通常,會選擇動態代理方式。這時,就需要反射技術來實現了。

 

情景三:避免將程序寫死到代碼裏

因爲java代碼是先通過編譯器將.java文件編譯成.class的二進制字節碼文件,因此如果我們使用new Person()來實例化對象person會出現的問題就是如果我們希望更換person的實例對象,就要在源代碼種更改然後重新編譯再運行,但是如果我們將person的實例對象類名等信息編寫在配置文件中,利用反射的Class.forName(className)方法來實例化java對象(因爲實例化java對象都是根據全限定名查找到jvm內存中的class對象,並根據class對象中的累信息實例化得到java對象,因此xml文件中只要包含了權限定類名就可以通過反射實例化java對象),那麼我們就可以更改配置文件,無需重新編譯。例如:

  • 開發通用框架 - 反射最重要的用途就是開發各種通用框架。很多框架(比如 Spring)都是配置化的(比如通過 XML 文件配置 JavaBean、Filter 等),爲了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法,這個時候就必須用到反射——運行時動態加載需要加載的對象。

 

反射的缺點

  • 性能開銷 - 由於反射涉及動態解析的類型,因此無法執行某些 Java 虛擬機優化。因此,反射操作的性能要比非反射操作的性能要差,應該在性能敏感的應用程序中頻繁調用的代碼段中避免。
  • 破壞封裝性 - 反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。
  • 內部曝光 - 由於反射允許代碼執行在非反射代碼中非法的操作,例如訪問私有字段和方法,所以反射的使用可能會導致意想不到的副作用,這可能會導致代碼功能失常並可能破壞可移植性。反射代碼打破了抽象,因此可能會隨着平臺的升級而改變行爲。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章