Java程序員都要懂得知識點:反射

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

本文分享自華爲雲社區《java知識點問題精選之反射》,原文做者:breakDraw 。html

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

反射就是把java類中的各類成分映射成一個個的Java對象。segmentfault

例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術能夠對一個類進行解剖,把個個組成部分映射成一個個對象。數組

(其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)緩存

反射

Q: 調用類對象.class 和 forName(類名)的區別?安全

Class<A> classA = A.class;
Class<A> classA = Class.forName("A");

A: 僅使用.class不能進行第一次靜態初始化, forname函數則能夠多線程

例如B是A的基類,下面這段代碼如何?
假設有父子2個類,以下:jvm

static class Parent { }

static class Son extends Parent{}

Q: 用instanceof 能夠和父類比較嗎,且會返回true嗎?函數

Son son = new Son();
        if (son instanceof  Parent) {
            System.out.println("a instanof B");
        }

A: 能夠比較,且返回true。性能

Q: 用getClass並用== 能夠和父類比較嗎,且會返回true嗎,下面這樣:
注意A是B的子類。

Son son = new Son();
        if (son.getClass() == Parent.class){
            System.out.println("son class == Parent.class");
        }

A: 不能夠,編譯就會報錯了。和Class<泛型>的 ==號比較有關。

由於getClass返回的是<? extends Son>, .class返回的是Class<Parent>

Q: 用getClass並用.equals能夠和父類比較嗎,且會返回true嗎,下面這樣:

Son son = new Son();
        if (son.getClass().equals(Parent.class)){
            System.out.println("son class.equals(Parent.class)");
        }

A: 能夠比較,正常編譯, 可是會返回false,即不相等!

Q: getDeclaredXXX 有哪幾種?
A: 5種:

  • 註解Annotation
  • 內部類Classed
  • 構造方法Construcotor
  • 字段Field
  • 方法Method

Q:getMethods()返回哪些方法, getDeclaredMethods()會返回哪些方法?

A:
getMethods()返回 本類、父類、父接口 的public方法
getDeclaredMethods()只 返回本類的 全部 方法

其餘getXXX和getDeclaredXXX的區別同理。

拿到Filed、Method、Constructor以後咋用

  • Method能夠invoke(object, args)
  • Constructor能夠newInstance(Object…)來作構造調用。
  • Filed能夠用get(object)、set(object)來設置屬性值。

Q: 反射拿到Method對象後, 該對象.getModifiers() 是幹嗎的?
A: 返回該方法的修飾符,而且是1個整數。

Q:
下面這段代碼會發生什麼?

package com.huawei.test

public class A {
    public A(int i ) {
        System.out.printf("i=" +i);
    }

    public static void main(String[] args) {
        try {
            A a = (A)Class.forName("com.huawei.test.A").newInstance();
        } catch (ClassNotFoundException e) {
            System.out.printf("ClassNotFoundException");
        } catch (InstantiationException e) {
            System.out.printf("InstantiationException");
        } catch (IllegalAccessException e) {
            System.out.printf("IllegalAccessException");
        }
    }
}

A:
打印InstantiationException初始化錯誤。由於A沒有默認構造器了,因此不能夠用newInstance來構造。應該改爲這樣,經過獲取正確的構造器來進行構造。

A a = (A)Class.forName("A").getConstructor(int.class).newInstance(123);

Q:如何提升反射的效率?
A:

  • 使用高性能反射包,例如ReflectASM
  • 緩存反射的對象,避免每次都要重複去字節碼中獲取。(緩存!緩存!)
  • method反射可設置method.setAccessible(true)來關閉安全檢查。
  • 儘可能不要getMethods()後再遍歷篩選,而直接用getMethod(methodName)來根據方法名獲取方法
  • 利用hotspot虛擬機中的反射優化技術(jit技術)
    參考資料: https://segmentfault.com/q/1010000003004720
    https://www.cnblogs.com/coding-night/p/10772631.html

Q:
用反射獲取到的method對象, 是返回一個method引用,仍是返回1個拷貝的method對象?
A:
反射拿method對象時, 會作一次拷貝,而不是直接返回引用,所以最好對頻繁使用的同一個method作緩存,而不是每次都去查找。

Q:
getMethods()後本身作遍歷獲取方法,和getMethod(methodName) 直接獲取方法, 爲何性能會有差別?
A:
getMethods() 返回method數組時,每一個method都作了一次拷貝。 getMethod(methodName)只會返回那個方法的拷貝, 性能的差別就體如今拷貝上。

Q:
獲取方法時,jvm內部其實有緩存,可是返回給外部時依然會作拷貝。那麼該method的緩存是持久存在的嗎?
A:
不是持久存在的,內存不足時會被回收。源碼以下:

private Class.ReflectionData<T> reflectionData() {
    SoftReference<Class.ReflectionData<T>> reflectionData = this.reflectionData;
    int classRedefinedCount = this.classRedefinedCount;
    Class.ReflectionData rd;
    return reflectionData != null && (rd = (Class.ReflectionData)reflectionData.get()) != null
    && rd.redefinedCount == classRedefinedCount ? rd : this.newReflectionData(reflectionData,     classRedefinedCount);
}

能夠看到這是一個軟引用。

軟引用的定義:內存緊張時可能會被回收,不過也能夠經過-XX:SoftRefLRUPolicyMSPerMB參數控制回收的時機,只要發生GC就會將其回收。

若是reflectionData被回收以後,又執行了反射方法,那隻能經過newReflectionData方法從新建立一個這樣的對象了。

Q: 反射是線程安全的嗎?
A:
是線程安全的。 獲取反射的數據時,經過cas去獲取。 cas概念能夠見多線程一節。

Q:
a普通方法調用
b反射方法調用
c關閉安全檢查的反射方法調用,性能差別以下:

b反射方法調用和c關閉安全檢查的反射方法調用的性能差別在哪?普通方法調用和關閉安全檢查的反射方法調用的性能差別在哪?
A:

  • 安全檢查的性能消耗在於
    ,SecurityManager.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); 這項檢測須要運行時申請RuntimePermission(「accessDeclaredMembers」)。 因此若是不考慮安全檢查, 對反射方法調用invoke時, 應當設置 Method#setAccessible(true)
  • 普通方法和反射方法的性能差別在於
  1. Method#invoke 方法會對參數作封裝和解封操做
  2. 須要檢查方法可見性
  3. 須要校驗參數
  4. 反射方法難之內聯
  5. JIT 沒法優化

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

相關文章
相關標籤/搜索