1. 程式人生 > >Java的反射機制和使用

Java的反射機制和使用

文章目錄

反射相信大家平時學習時用的不多但見的很多,特別是各種開源框架中,到此都是反射。編譯時載入類是靜態載入、執行時載入類是動態載入,動態載入可通過反射實現。

一、定義

  • 反射機制是在執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法
  • 對於任意一個物件,都能夠呼叫它的任意一個方法
  • 在java 中,只要給定類的名字,那麼就可以通過反射機制來獲得類的所有資訊

二、功能

  • 在執行時判定任意一個物件所屬的類;
  • 在執行時建立物件;
  • 在執行時判定任意一個類所具有的成員變數和方法;
  • 在執行時呼叫任意一個物件的方法;
  • 生成動態代理。

如:Class.forName(‘com.mysql.jdbc.Driver.class’);//載入MySql 的驅動類。這就
是反射,現在很多框架都用到反射機制,hibernate,struts ,spring等框架都是用反射機制實
現的。

三、反射的實現方式

1、Class.forName(“類的路徑”)
2、類名.class
3、物件名.getClass()
4、如果是基本型別的包裝類,則可以通過呼叫包裝類的Type 屬性來獲得該包裝類的Class 物件。
​ 例如:Class<?> clazz = Integer.TYPE;

這個Class也稱類型別,也就是說每一個 Class它也是個物件(萬物皆物件),所以clazz是一個類物件

四、實現反射的類

1、Class:它表示正在執行的Java 應用程式中的類和介面。
2、Field:提供有關類或介面的屬性資訊,以及對它的動態訪問許可權,如private、public等許可權。
3、Constructor:提供關於類的單個構造方法的資訊以及對它的訪問許可權
4、Method:提供關於類或介面中某個方法資訊

注意:Class類是Java反射中最重要的一個功能類,所有獲取物件的資訊(包括:方法/屬性/構造方法/訪問許可權)都需要它來實現。

五、Java動態載入類使用場景

例如,你可以傳一個引數,執行時判斷引數為1載入A類(通過反射),引數為2載入B類,然後A、B類都實現一個介面C,這樣就可以面向介面程式設計啦(主程式面向C程式設計,後面還可以繼續新增C介面的實現類來擴充套件,符合開閉原則)

六、反射機制的優缺點?

優點:
(1)能夠執行時動態獲取類的例項,大大提高程式的靈活性(由各框架中到此是反射可見)。
(2)與Java 動態編譯相結合,可以實現無比強大的功能。
缺點:
(1)使用反射的效能較低。java 反射是要解析位元組碼,將記憶體中的物件進行解析。

七、下面不是概念,乾貨來啦!

這是我寫的一個反射工具測試類

package com.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * @author Guoming Zhang
 * @Description Class類的使用
 * @Date 2018/12/20
 */
class Foo {
    void print(int a, int b) {
        System.out.println(a + b);
    }

    void print(String a, String b) {
        System.out.println(a + " , " + b);
    }
}

public class ReflectUtil {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        /***********一、反射Class類的使用*****************/
        //useOfClass();

        /***********二、反射獲取方法資訊******************/
        //printMethodMessage(new String());

        /***********三、反射獲取成員變數********************************/
        //printFiledMessage(new String());

        /***********四、反射獲取成員建構函式********************************/
        //printConstructorMessage(new String());

        /***********五、方法的反射呼叫********************************/
        //printMethodInvoke();

        /***********六、通過反射驗證泛型擦除**********************************************/
        printFanXingClear();
    }

    //反射Class類的使用
    public static void useOfClass() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Foo foo = new Foo();

        Class c1 = Foo.class;
        Class c2 = foo.getClass();
        Class c3 = Class.forName("com.reflect.Foo");

        System.out.println(c1 == c2);//true
        System.out.println(c2 == c3);//true

        //可以通過類型別建立物件
        Foo myFoo = (Foo) c3.newInstance();
        myFoo.print(1, 2); // "你好啊朋友"
    }

    //反射獲取方法資訊
    public static void printMethodMessage(Object obj) {
        //獲取位元組碼,或稱類型別
        Class c = obj.getClass();
        System.out.println("類的名稱為:" + c.getName());//c.getSimpleName()則不帶包名

        /**
         * Method : 方法物件,方法也是一個物件!萬物皆物件
         * getMethods獲取所有public方法,包括父類繼承而來的
         * getDeclareMethods() 獲取所有自己宣告的方法,任何訪問許可權,不包父類的
         */
        Method[] ms = c.getMethods();//或c.getDeclareMethods()
        for (int i = 0; i < ms.length; i++) {
            //得到方法的返回值型別的類型別,如方法返回一個String,則為String.class
            Class returnType = ms[i].getReturnType();
            //獲得類型別的不帶包名的名字,getName()則為帶包名的名字
            System.out.print(returnType.getSimpleName() + "  ");
            //得到方法的名稱
            System.out.print(ms[i].getName() + " (");
            //獲取引數型別的類型別
            Class[] paramTypes = ms[i].getParameterTypes();
            for (Class clazz : paramTypes) {
                System.out.print(clazz.getSimpleName() + " , ");
            }
            System.out.println(" )");
        }
    }

    //反射獲取成員變數
    public static void printFiledMessage(Object obj) {
        //獲取位元組碼,或稱類型別
        Class c = obj.getClass();
        System.out.println("類的名稱為:" + c.getName());//c.getSimpleName()則不帶包名

        /**
         * 成員變數也是物件,萬物皆物件
         * getFields()獲取所有public的成員變數資訊,包括父類的
         * getDeclaredFields()獲取自己宣告的所有許可權的成員變數資訊
         */
        Field[] fs = c.getDeclaredFields();
        for (Field field : fs) {
            //獲得成員變數的型別的類型別
            Class fieldType = field.getType();
            //獲得成員變數型別的不帶包名的名稱
            String typeName = fieldType.getSimpleName();
            //獲得成員變數的名稱
            String fieldName = field.getName();
            System.out.println(typeName + " " + fieldName);
        }
    }

    //反射獲取成員建構函式
    public static void printConstructorMessage(Object obj) {
        //獲取位元組碼,或稱類型別
        Class c = obj.getClass();
        System.out.println("類的名稱為:" + c.getName());//c.getSimpleName()則不帶包名

        /**
         * 建構函式也是物件,萬物皆物件!
         * getConstructors獲取所有public的建構函式,包括父類的
         * getDeclaredConstructors()獲取自己宣告的所有許可權的建構函式,不包父類的
         */
        Constructor[] cs = c.getDeclaredConstructors();
        for (Constructor constructor : cs) {
            System.out.print(constructor.getName() + " (");
            //獲取建構函式的引數列表
            Class[] paramTypes = constructor.getParameterTypes();
            for (Class clazz : paramTypes) {
                System.out.print(clazz.getSimpleName() + " ,");
            }
            System.out.println(" )");
        }
    }

    //方法的反射呼叫
    public static void printMethodInvoke() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Foo foo = new Foo();
        //要獲取一個類的方法,先要獲取類型別(或稱位元組碼)
        Class c = foo.getClass();
        /**
         * getMethod()獲取的是所有public方法,包父類的
         * getDeclaredMethod()獲取自己宣告的所有許可權方法,不包父類的
         */
        Method method = c.getDeclaredMethod("print", int.class, int.class);
        //方法的反射呼叫 用Method物件來進行呼叫
        //如果方法沒有返回值返回null,否則返回具體的返回值
        Object o = method.invoke(foo, 10, 20);
    }

    //通過反射驗證泛型擦除
    public static void printFanXingClear() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<String> list = new ArrayList<>();
        list.add("你好");
        /**
         * list.add(45);是不是會出錯?
         * 但是!泛型是防止錯誤輸入的,編譯後泛型擦除,不存在泛型
         * 下面在執行時通過反射證明
         */
         Class clazz = list.getClass();

         Method method = clazz.getMethod("add",Object.class);
         method.invoke(list,new Integer(45));

        System.out.println(list);
    }
}