Java中反射的作用

反射的主要作用就是獲取JVM中的Class對象,獲取了對象之後可以實現很多功能,比方說IOC容器通過反射創建對象、或者是動態代理(這個實際應用講完反射原理後稍微再講一下)。

一、獲取Class對象的三種方式:

如何獲取Class對象呢?有三種情況:

  1. Java代碼僅經過編譯生成了字節碼文件,還未加載到內存中
  2. Java代碼加載到了內存中,但是還未創建對象實例
  3. 已經創建了對象實例

針對這三種情況分別有以下三種方式獲取Class對象:

1、Class.forName(“全類名”):只有字節碼文件,還未被加載進內存。多用於配置文件,將類名定義在配置文件中

2、類名.class:已經加載進內存但是還沒有對象。多用於參數的傳遞

3、對象名.getClass():有對象希望獲取字節碼。

同一個字節碼文件在一次程序的運行過程中只會被加載一次。不論哪一種方式獲取的class類對象都是同一個,即這三種方式只會創建一個Class對象

二、Class對象的功能(即Class類中有哪些常用的方法)

以下代碼均假設已經獲取到了Person類的Class對象:personClass

1、獲取類名

  • String getName()
// 獲取到的是全類名
String className = personClass.getName();

2、獲取構造函數

反射獲取Class對象後最重要的一個功能就是創建對象,創建對象自然是要通過構造函數。
方法

  • Constructor<?>[] getConstructors:獲取所有public修飾的構造函數

  • Constructor<T> getConstructor(類<?>...parameterTypes)獲取指定的public修飾的構造函數

    參數類<?>...parameterTypes表示要傳構造函數參數的類型,因爲同一個類中區分構造函數只能通過參數
    如要獲取構造函數Person(String name, int get),則personClass.getConstructor(String.class, int.class)

  • Constructor<?>[] getDeclaredConstructors():獲取所有的構造函數

  • Constructor<T> getDeclaredConstructor(類<?>...parameterTypes):獲取任一指定的構造函數

作用

1、獲取構造函數當然是爲了創建對象

  • T newInstance(parameters...)
  • Class.newInstance():調用空參的構造方法可以這樣簡化,但是這個方法好像JDK9之後被廢除了。
Person p = new Person;
// 空參構造函數簡化調用:personClass.newInstance();
// 假設獲取的構造方法有兩個參數:Stirng類型的名字和int類型的年齡
Constructor constructor = personClass.getConstructor(String.class, int.class);
constructor.newInstance("martin", 23);

私有構造函數可以通過getDeclaredConstructor()獲取Constructor對象,但是不能constructor.newInstance()調用,會報IllegalAccessException(非法訪問異常),可以通過執行constructor.setAccessible(true)忽略訪問權限修飾符的安全檢查

3、獲取成員變量

方法

  • Field[] getFields():獲取所有public修飾的成員變量
  • Field getField(String name):獲取所有指定的public修飾的成員變量
  • Field[] getDeclaredFields():獲取所有的成員變量
  • Field getDeclaredField(String name):獲取任一指定的成員變量

作用

1、取值get()、賦值set()

Field field = personClass.getField("name");
Person p = new Person;
Object value = field.get(p); // 獲取對象p的name屬性
name.set(field, "martin"); // 設置對象p的name屬性

同理可以通過field .setAccessible(true)暴力反射

4、獲取成員方法

如何:

  • Method[] getMethods():獲取所有public修飾的成員函數,包括從父類如Object等類中繼承的
  • Method getMethod(String name, 類<?>...parameterTypes)
    獲取所有public修飾的指定的成員函數。第一個參數爲函數方法名,後面的參數是函數的參數類型。
  • Method[] getDeclaredMethods():獲取本類中所有方法,不會獲取父類中的
  • Method[] getDeclaredMethod(String name, 類<?>...parameterTypes):獲取本類中任一指定的方法

同理,私有方法可以通過setAccessible(true)暴力反射

作用:

1、獲取方法名

Method.getName()

2、執行方法

Method.invoke()

Person p = new Person;
// 獲取eat()的方法對象
Method method_eat = personClass.getMethod("eat");
method_eat.invoke(p);

// 獲取eat(String foodName)的方法對象
Method method_eat = personClass.getMethod("eat", String.class);
method_eat.invoke(p, "rice");

三、案例

1、jdbc連接數據庫中通過Class.forName()加載數據庫驅動

2、代理模式對方法增強(aop的原理也是動態代理),這裏稍微看一下動態代理中的JDK代理,先上關鍵部分的代碼

public class StarProxy implements InvocationHandler {
	// target接受的是接口,JDK動態代理面向接口代理
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 加強操作
        System.out.println("收代理費");
        // 調用原方法
        Object result = method.invoke(target, args);
        //加強操作
        System.out.println("互換名片");
        return result;
    }

    // 這個方法返回的是代理對象實例,newProxyInstance()深入進去就是根據反射調用了構造函數生成實例
	//PS:生成代理類的方法不一定寫在這裏,可以寫在一個工廠方法中
    public Object CreateProxyObject() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

文章是看了視頻總結,視頻講的很好,附上視頻鏈接

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