JAVA反射機制

 反射可讓咱們在運行時動態的加載類,訪問、調用類的字段、方法、構造器。反射機制是構建框架的基礎,如Spring框架核心,各類RPC框架等。java

Class:class的抽象

 首先咱們須要知道在java中,類的信息是由Class對象來描述的,每一個類對應一個Class對象,它表示一個類或者接口(一個annotation也算是一個接口)在運行時的類型信息。Class只有一個private的構造器,你並不能手動的去建立它,Class對象只能由JVM在加載一個類或者是在調用一個方法的時候去建立它。  獲取一個類的Class對象有如下幾種方式:安全

  • Class.forName(String className)
  • obj.getClass()
  • ClassName.class

 相較於forName()的方式,.class方式能得到編譯期的檢查更加安全。以上三種方式均可以得到類的Class對象,可是他們之間也有一些差異,Class.forName(String className)默認會對這個類進行初始化操做,而.class的方式則不會自動初始化該Class對象。網絡

import java.util.Random;

public class InitObj1 {

    public static final int value1 = 1;//編譯期常量,不須要初始化

    public static final int value2 = new Random().nextInt();//須要初始化

    static{
        System.out.println("static block initializing.");
    }
}
public class InitObj2 {
    public static final int value1 = 1;

    static{
        System.out.println("static block initializing.");
    }
}
public class ClassInitTest {

    public static void main(String[] args) throws ClassNotFoundException {

        System.out.println("----經過.class獲取Class對象-----");
        Class c1 = InitObj1.class;
        System.out.println("created c1");
        System.out.println("value1:" + InitObj1.value1);
        System.out.println("value2:" + InitObj1.value2);

        System.out.println("----經過forName獲取Class對象-----");
        Class c2 = Class.forName("me.l4j.thinkingInJava.reflectionAPI.InitObj2");
        System.out.println("created c2");
        System.out.println("value1:" + InitObj2.value1);
    }
}

輸出:
----經過.class獲取Class對象-----
created c1
value1:1
static block initializing.
value2:-2130950913
----經過forName獲取Class對象-----
static block initializing.
created c2
value1:1框架

從結果能夠看到.class獲取Class引用不會引起初始化,延遲到了對value2的訪問,可是forName()方法會當即引起初始化。dom

獲取類型信息的簡單示例

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * Created by lsj on 2017-9-6.
 */
public class ReflectString {

    public static void main(String[] args) {

        Class<?> strC = String.class;

        //獲取類名
        System.out.println(strC.getName());

        //獲取實現的接口
        Class<?>[] inter = strC.getInterfaces();
        System.out.println("String implements interfaces:");
        for (Class c : inter)
            System.out.println("    --" + c.getName());

        //getXXX方法只能獲取到public,getDeclaredXXX能夠獲取到全部定義了的元素
        //獲取構造器
        Constructor<?>[] cons = strC.getDeclaredConstructors();
        System.out.println("All Constructors:");
        for (Constructor csr : cons) {
            System.out.println("    constructor name:"+csr.getName());
            System.out.println("    constructor modifier:" + Modifier.toString(csr.getModifiers()));
            Arrays.stream(csr.getParameterTypes()).forEach(x-> System.out.println("    param:"+x.getName()));

            System.out.println("    ------------------------------------");
        }

        //獲取字段
        Field[] fields = strC.getDeclaredFields();

        System.out.println("All fields:");
        for (Field f : fields) {
            System.out.println("    name:" + f.getName());
            System.out.println("    modifier:"+Modifier.toString(f.getModifiers()));
            System.out.println("    ------------------------------------");
        }


        //獲取方法
        Method[] methods = strC.getDeclaredMethods();
        System.out.println("All methods:");
        for (Method m : methods){
            System.out.println("    name:"+m.getName());
            System.out.println("    modifier:"+Modifier.toString(m.getModifiers()));
            System.out.println("    returnType:"+m.getReturnType().getName());
            Arrays.stream(m.getParameterTypes()).forEach(x-> System.out.println("    param:"+x.getName()));
            Arrays.stream(m.getAnnotations()).forEach(x-> System.out.println("    annotation:"+x.annotationType().getName()));
            System.out.println("    ------------------------------------");
        }

        // 建立一個String對象cs,向上轉型爲CharSequence,其類型信息任然是一個String
        CharSequence cs = new String("str");
        Class csClass = cs.getClass();
        System.out.println(csClass.getName());

    }
}

類型判斷

 對類型的判斷主要有istanceof關鍵字、Class.isInstance(Object obj)方法和"=="三種方式。ide

import java.io.Serializable;

public class ClassType {
    public static void main(String[] args) {

        //String和StringBuilder都實現了CharSequence接口和Serializable接口,將str和sb都向上轉型爲一個CharSequence
        CharSequence str = new String();
        CharSequence sb = new StringBuilder();

        //istanceof和isInstance()表示這個對象是不是這個類或者接口的實例,即這個對象是不是這個類型
        System.out.println("str instanceof CharSequence:" + (str instanceof CharSequence));
        System.out.println("str instanceof String:" + (str instanceof String));
        System.out.println("str instanceof Serializable:" + (str instanceof Serializable));

        System.out.println();

        System.out.println("CharSequence.class.isInstance(str): "+CharSequence.class.isInstance(str));
        System.out.println("String.class.isInstance(str): "+String.class.isInstance(str));

        System.out.println();

        //==只能與對象實際類型相同時才能返回true
        System.out.println("String.class == str.getClass(): " + (String.class == str.getClass()));
        System.out.println("CharSequence.class == str.getClass(): " + (CharSequence.class == str.getClass()));


        //雖然str和sb都向上轉型爲CharSequence,可是他們兩的實際類型並不相同
        System.out.println("str.getClass(): "+str.getClass());
        System.out.println("sb.getClass(): "+sb.getClass());
    }
}

instanceofisInstance()保持了類型的概念(是不是這個類,或是不是這個類的派生類),而==比較的是實際的class對象。工具

類型轉換

 由於知道一隻狗必定是一隻動物,因此編譯器容許自由的向上轉型,不須要任何顯示的轉換。可是一直動物是否是一隻狗這是不肯定的,在進行「向下轉型」的時候須要顯式的進行轉換(Dog)object,若是這個object並不屬於Dog,那麼在運行時會觸發ClassCastException異常。在進行「向下轉型」時咱們能夠先使用instanceof或者isInstance()進行類型檢查,以免觸發異常。  在JAVA SE5開始咱們可使用Class引用的cast()進行轉型,暫沒有發現這種轉型語法的特異功能。ui

Animal a = new Dog();
Class<?> dogCls = Dog.class;
Dog d = dogCls.cast(a);

反射

 經過java反射機制,咱們能夠動態的建立對象,體現出很大的靈活性。相較於靜態編譯,經過反射執行的操做要慢得多。  java在java.lang.reflect包下提供了反射的工具和接口,reflect包只是java反射機制的一部分,其餘還包括前面提到的Class類以及Object類等。this

ReflectionAPI簡介

 在reflect包中Field、Method和Constructor這三個類比較經常使用。咱們能夠經過Constrcutor建立新的對象(Class引用的newInstance方法也能夠建立對象,可是須要這個類有無參構造器),利用Field中的set()get()方法來讀取和修改字段,以及使用invoke()方法來調用與Method關聯的方法。下面的實例展現了這三個類的基本用法:操作系統

import java.util.List;

public class Student {
    private String name;
    private int age;
    private List<String> courses;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Student(){
        this.name = "John Doe";
        this.age = -1;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getCourses() {
        return courses;
    }

    public void setCourses(List<String> courses) {
        this.courses = courses;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", courses=" + courses +
                '}';
    }
}
import java.lang.reflect.*;
import java.util.Arrays;

public class ReflectAPI {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

        // 利用泛型的Class來指明這個Class對象爲Student的Class對象
        Class<Student> stCls = Student.class;

        // 利用Class的newInstance()方法,調用無參構造器來建立對象
        Student sDefault = stCls.newInstance();
        System.out.println("調用無參構造器建立Student對象: "+sDefault);

        // 獲取帶String和int類型參數的構造器
        Constructor ctr = stCls.getConstructor(String.class, int.class);
        Student s = (Student)ctr.newInstance("john", 20);
        System.out.println(s);


        // 爲s這個對象增長課程
        // Student中courses字段爲private,getField("courses")會觸發NoSuchFieldException異常
        // Field fld = stCls.getField("courses");
        Field fld = stCls.getDeclaredField("courses");

        // 因爲courses字段爲private的,不設置Field.setAccessible(true)會觸發IllegalAccessException異常
        fld.setAccessible(true);
        fld.set(s, Arrays.asList("計算機網絡","操做系統"));
        System.out.println(fld.get(s));

        // 設置爲private的字段,通常來講是不但願你去直接訪問它的。
        // 一種合法的修改這個值的方法就是調用其public的set方法去修改它
        // 這裏使用getMethod()方法去獲取修飾爲public的方法,若是這個方法爲private或者不存在會觸發NoSuchFieldException異常
        Method m = stCls.getMethod("setAge", int.class);
        m.invoke(s, 24);
        System.out.println(s);
    }
}

動態代理

 首先了解下什麼是代理模式:其目的是爲其餘對象提供一個代理以控制對某個對象的訪問。代理類負責爲委託類預處理消息,過濾消息並轉發消息,以及進行消息被委託類執行後的後續處理。而動態代理能夠動態的建立代理並動態的處理對所代理方法的調用。  JDK實現的動態代理只能代理實現了某接口的被代理對象。java動態代理主要用到java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。動態代理的實現主要包括如下步驟:

  1. 實現InvocationHandler接口建立調用處理器
  2. 調用Proxy的newInstance()方法建立動態代理實例

 如下是一個動態代理的示例程序:

public interface WorkerInterface {
    void doJob(String job);
}
public class Worker implements WorkerInterface{
    @Override
    public void doJob(String job) {
        System.out.println("doing "+job);
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyProxyHandler implements InvocationHandler {

    private Object proxied;

    public MyProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----------------info------------------------");
        System.out.println("Proxy: "+proxy.getClass());
        System.out.println("Method: "+method);
        System.out.println("args: "+args);
        System.out.println("----------------info------------------------");
        return method.invoke(proxied, args);
    }
}
public class SimpleProxy{
    public static void doJob(WorkerInterface wi) {
        System.out.println("Get ready to start work");
        wi.doJob("Whitewashing");
        System.out.println("Finish the work");
    }

    public static void main(String[] args) {
        Worker w = new Worker();

        WorkerInterface proxy = (WorkerInterface)Proxy.newProxyInstance(WorkerInterface.class.getClassLoader(),
                new Class[]{WorkerInterface.class},
                new MyProxyHandler(w));

        doJob(proxy);
    }
}

 咱們在調用Proxy.newInstance方法時其實是經歷了一下三步:

  1. 建立代理類的類對象Class<?> cl = getProxyClass0(loader, intfs)
  2. 反射從生成的類對象cls中獲取構造器對象:Constructor<?> cons = cl.getConstructor(constructorParams)
  3. 用上一步獲取的構造器對象建立動態代理類實例cons.newInstance(new Object[]{h})

 經過動態代理咱們能夠爲目標類預處理消息、添加修飾等。

相關文章
相關標籤/搜索