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);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章