Java反射知識總結

在不少狀況下,咱們知道如何編寫反射相關類或者方法,可是沒法口述反射是什麼,裏面的機制是什麼,下面我先如淺入深介紹反射。設計模式

一、定義

反射 (Reflection) 是 Java 的特徵之一,它容許運行中動態加載Java 程序獲取自身的信息,而且能夠操做類或對象的內部屬性,是一種功能強大且複雜的機制,能夠用在下面四個方面:數組

  • 在運行時分析類的能力
  • 在運行時查看對象
  • 實現通用的數組操做代碼
  • 利用Method對象

上面是正式的反射定義,比較抽象,用個大白話來講,反射就是動態加載類、調用方法和訪問屬性,它不須要事先知道運行對象是誰。反射的重點在運行時而不是編譯時,也就是動態加載。下面咱們先來看下一個簡單的例子bash

// 普通new
User user = new User();
user.setEmail("xxxx@xxx.com");

// 反射例子
try {
    Class clz = Class.forName("com.fomin.demo.model.User");
    Method method = clz.getMethod("setEmail", String.class);
    Constructor constructor = clz.getConstructor();
    Object object = constructor.newInstance();
    method.invoke(object, "aaa@xx.com");
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
        | InstantiationException | InvocationTargetException e) {
    e.printStackTrace();
}
複製代碼

上面須要一個User實例,正常狀況下,咱們只須要new一個實例就行,但咱們也會遇到一些特殊狀況,我不知道初始化的類對象是什麼,沒法使用new進行實例化,這就須要在動態運行時才須要建立一個實例。框架

二、用法

Java的反射機制的實現要藉助於4個類:Class,Constructor,Field,Method,而且若是須要建立實例,可使用newInstance建立,例如,clz.newInstance或者constructor.newInstance
複製代碼
  • Class:表明的是類對象,反射的核心。在反射中,首先須要獲取到該類的Class對象。有三種實現方式
//使用 Class.forName 靜態方法
Class clz = Class.forName("com.fomin.demo.model.User");

//使用 .class 方法
Class clz = User.class;

//使用類對象的 getClass() 方法
User user = new User();
Class clz = user.getClass();
複製代碼
  • Constructor:類的構造器對象,用來獲取構造函數方法,使用方式有如下幾種:
//獲取指定參數得到public構造函數
Constructor<T> getConstructor(Class<?>... parameterTypes);

//根據指定參數獲取public和非public的構造函數
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
複製代碼
  • Field:類的屬性對象
//獲取類的全部聲明字段,但不能獲得其父類的成員變量,包含私有的
Field[] getDeclaredFields()

//獲取指定參數的類的全部聲明字段,但不能獲得其父類的成員變量,包含私有的
Field getDeclaredField(String name)

//獲取類的全部公有聲明字段,但不能獲得其父類的成員變量
Field[] getFields()

//獲取指定參數的類的全部公有聲明字段,但不能獲得其父類的成員變量
Field getField(String name)
複製代碼
  • Method:類的方法對象
//獲取類的全部公用(public)方法,包括其繼承類的公用方法
Method[] getMethods()

//獲取特定的公用(public)方法,第一個參數爲方法名稱,後面的參數爲方法的參數對應Class的對象
Method getMethod(String name, Class<?>... parameterTypes)

//獲取類或接口聲明的全部方法,包括公共、保護、默認訪問和私有方法,不包括繼承方法
Method[] getDeclaredMethods()

//獲取特定的方法,第一個參數爲方法名稱,後面的參數爲方法的參數對應Class的對象
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
複製代碼

**三、**invoke

屬於Method的一個方法,用來反射執行方法的,包含兩個參數,一個是Object obj表示被調用方法底層所屬對象,Object... args表示調用方法是傳遞的實際參數。 invoke中核心在MethodAccessorImpl,NativeMethodAccessorImpl,DelegatingMethodAccessorImpl這三個類中,其中NativeMethodAccessorImpl和DelegatingMethodAccessorImpl繼承了抽象類MethodAccessorImpl。而且 NativeMethodAccessorImpl 對象交給 DelegatingMethodAccessorImpl 對象代理。 在執行invoke時,首先須要對非重寫方法檢查權限,對非public方法,進行二次檢查checkAccess。ide

if (!override) {
    if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
        Class<?> caller = Reflection.getCallerClass();
        checkAccess(caller, clazz, obj, modifiers);
    }
}
複製代碼

獲取當前的MethodAccessor,若是是空的,調用acquireMethodAccessor建立,首先先從root獲取,仍是爲空調用ReflectionFactory.newMethodAccessor建立一個,並設置在root中。模塊化

MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
    methodAccessor = tmp;
} else {
    // Otherwise fabricate one and propagate it up to the root
    tmp = reflectionFactory.newMethodAccessor(this);
    setMethodAccessor(tmp);
}
複製代碼

最後調用NativeMethodAccessorImpl中的native方法invoke0函數

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
    if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
        MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
        this.parent.setDelegate(var3);
    }

    return invoke0(this.method, var1, var2);
}

void setParent(DelegatingMethodAccessorImpl var1) {
    this.parent = var1;
}

private static native Object invoke0(Method var0, Object var1, Object[] var2);
複製代碼

四、總結

反射使用的地方不少,例如AOP思想就是須要用到發射、模塊化的開發,經過反射去調用對應的字節碼;動態代理設計模式也採用了反射機制, Spring/Hibernate 等框架。 優勢性能

  • 無需知道對象是什麼,可使用其內部相關的方法、屬性等
  • 提升代碼的靈活度,可用於通用模塊開發
  • 提升擴展性,下降耦合性,提升自適應能力

缺點:ui

  • 性能消耗大,慢於直接使用類、方法、屬性等性能
  • 增長邏輯難度,會帶來維護困難
相關文章
相關標籤/搜索