基礎篇:深刻解析JAVA反射機制

反射的概念

  • java的放射機制:在程序運行時,程序有能力獲取一個類的全部方法和屬性;而且對於任意一個對象,能夠調用它的任意方法或者獲取其屬性
  • 通俗解析:java文件須要編譯成.class文件才能被jvm加載使用,對象的.class數據在jvm裏就是Class<T>;咱們若是能拿到這個Class<T>對象,就能獲取該Class<T>對應的對象類型,及在該類型聲明的方法和屬性值;還能夠根據Class<T>建立相應的類型對象,經過Field,Method反過來操做對象
  • java相關類介紹
類名 描述
Class<T> 表明類的實體,在運行的Java應用程序中表示類或者接口
Field 類的成員變量(成員變量也稱爲類的屬性)
Method 類的方法
Constructor<T> 類的構造方法

獲取Class的三種方法

  • 1經過已知的類型獲取class
// 根據Example 獲取Class =》Example.class
public Class<Example> getExample(){
    Class<Example> clazz = Example.class;
    return clazz;
}
  • 2經過實例對象獲取class
public Class<Example> getExampleByInstance(){
    Example example = new Example();
    // getClass是Object類裏面的方法;《?》 是通配符
    Class<?> clazz = example.getClass();
    return (Class<Example>)clazz;
}
  • 3經過Class.forName獲取全路徑指定類名的class
/** forName0 本地方法,C++實現,jvm調用
 *    1 className 是個類名  2 initialize 是否延遲加載  3 loader 加載器
 */
private static native Class<?> forName0(String className, boolean initialize,
                ClassLoader loader, Class<?> caller) throws ClassNotFoundException;

public static Class<?> forName(String className) throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
// 兩個forName方法最終都會調用forName0方法去加載class   
public static Class<?> forName(String name,
        boolean initialize, ClassLoader loader) throws ClassNotFoundException {
        ....
        return forName0(name, initialize, loader, caller);
    }
// 示例:經過java.lang.Integer 
public Class<Integer> getInteger()throws ClassNotFoundException{
    Class<?> clazz = Class.forName("java.lang.Integer");
    return (Class<Integer>)clazz;
}

JAVA反射API

  • Class經常使用操做方法
//獲取全部的構造方法 / private public
public Constructor<?>[] getDeclaredConstructors()
//獲取特定的構造方法 / private public
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)    
//獲取類的父類
public native Class<? super T> getSuperclass()    
//獲取類實現的接口
private Class<?>[] getInterfaces(boolean cloneArray)  
//獲取在類內定義的內部類或接口
public Class<?>[] getDeclaredClasses()
//獲取全部的方法
public Method[] getDeclaredMethods() throws SecurityException
//根據方法名和參數得到特定的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)  
//獲取類型的定義的全部屬性
public Field[] getFields() throws SecurityException
// 根據屬性命名得到特定的Field
public Field getField(String name)
  • Method經常使用的操做方法
//得到方法的放回類型
public Class<?> getReturnType()   
//得到方法的傳入參數類型
public Class<?>[] getParameterTypes()   
//obj是實例對象,args是方法,反過來由Method控制對象的方法調用
public Object invoke(Object obj, Object... args)
  • Field經常使用的操做方法
//屬性與obj相等則返回true
public boolean equals(Object obj)
//得到obj中對應的屬性值
public Object get(Object obj)
//設置obj中對應屬性值
public void set(Object obj, Object value)
  • Constructor
//根據傳遞的參數建立類的對象:initargs 構造方法參數
public T newInstance(Object... initargs)
  • 1根據class建立對象
//方式一 clazz.newInstance()
Class<Example> clazz = Example.class;
Example example = clazz.newInstance();
//方式二 先獲取再由Constructor:clazz.getConstructors()/getConstructor(...) 
//再由Constructor.newInstance 方法構造對象
-----------------------------------------
public class Example {
    private int value;
    public Example(){ } // 若是隻聲明有參構造函數,clazz.newInstance()會報錯
    public Example(Integer value){  this.value  = value;  }
    static public void main(String[] args) throws Exception{
        Class<Example> clazz = Example.class;
        //根據指定構造函數參數獲取Constructor
        Constructor<Example> constructor = clazz.getConstructor(Integer.class);
        Example example = constructor.newInstance(100);
        System.out.println(example.value);
    }
}
  • 2由class獲取Field,並操做實例的屬性
public class Example {
    private int value , count;
    static public void main(String[] args) throws Exception{
        Class<Example> clazz = Example.class;
        //獲取全部的屬性,getField只能獲取public的屬性
        Field[] fs = clazz.getDeclaredFields();
        //根據名稱獲取指定 Field
        Field value = clazz.getDeclaredField("value");
        Example example = clazz.newInstance();
        //使用反射機制能夠打破封裝性,致使了java對象的屬性不安全  
        value.setAccessible(true); //setAccessible(true)讓private的參數可賦值操做
        //由Field反過去設置example的值
        value.set(example,100);
        System.out.println(example.value);
    }
}
  • 3由class獲取Method,並反射調用實例方法
public class Example {
    public static void main(String[] args) throws Exception {
        Class<Example> clazz = Example.class;
        Example example = clazz.newInstance();
        Method[] methods = clazz.getDeclaredMethods();
        //getDeclaredMethod和getMethod是:getMethod只能返回public的方法
        Method method = clazz.getDeclaredMethod("hello", String.class);
        method.setAccessible(true);
        method.invoke(example, "cscw");
    }
    private void hello(String name) { System.out.println(name + " Hello!"); }
}
-----
cscw Hello!

反射機制應用的場景

  • 1 動態拓展:假設有同一組類是實現相同的接口,而且類的加載方式不限制。當咱們須要那種具體類實現的功能時,只需加載.class文件,並獲取對應的Class<T>對象。能夠由Class或者Constructor實例化對象instance;根據接口定義,能夠獲取Class<T>裏的某一方法Method,並配合instance調用功能方法
  • 2 Spring的IOC就是基於反射機制實現
  • 3 JDK的動態代理

反射和JDK動態代理

  • 在Java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口。經過這個類和接口能夠生成JDK動態代理類或動態代理對象
public interface InvocationHandler {
    //全部方法都會調用此代理方法
    Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
}     
public class Proxy implements Serializable{
    ...
    //根據interfaces和InvocationHandler生成代理對象
    public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces, InvocationHandler h) 
    ...    
}
  • JDK的動態代理由Proxy和InvocationHandler實現;而被代理對象必須實現一個接口。代理對象由Proxy生成,可轉爲接口interface的實現類對象OBJ。當調用OBJ的方法時,則會觸發InvocationHandler.invoke,參數依次爲代理對象Method對象,和方法Method所需的參數。在invoke方法能夠加入拓展的邏輯,如日誌記錄操做;並能夠在invoke裏利用反射的技術調用 被代理對象方法
  • 示例
public class ExampleFactory<T> implements InvocationHandler{
    private T target;
    public T bind(T obj){
        target = obj;
        return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                    obj.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        //加強邏輯
        System.out.println("log start");
        //反射調用被代理對象方法
        Object result = method.invoke(target,objects);
        System.out.println("log end");
        return result;
    }
}
-----------
public interface Face {
    void hello(String name);
}
---------
//被代理對象必須實現一個接口,並由接口方法對方提供功能
public class Example implements Face {
    public void hello(String name) {
        System.out.println(name + " Hello!");
    }
    public static void main(String[] args)  {
        //ExampleFactory<Face> 至關於一箇中介人
        ExampleFactory<Face> factory = new ExampleFactory<>();
        //example 是代理對象
        Face example = exampleProxy.bind(new Example());
        example.hello("思婷");
    }
}
-----
log start
思婷 Hello!
log end

歡迎指正文中錯誤

關注公衆號,一塊兒交流

參考文章

相關文章
相關標籤/搜索