反射原來是這麼玩的(反射一開,誰都不愛)

反射的發展歷史

1996年01月23日,jdk 1.0版本發佈,代號爲Oak(橡樹)。

這個代號爲Oak(橡樹)的版本,在發佈後的第二年,1997年02月19日,發佈jdk 1.1版本,這次版本發佈中引入了反射機制。

關於反射機制,由於年代久遠,能搜索到對於反射機制的記載少之又少,能找到最爲久遠的是一篇題爲《Using Java Reflection》的文章,發表於 1998年1月,文中提到:反射是一個可以獲取java類、屬性的一個工具,因爲它是動態加載的

而在另外一篇文章《A Button is a Bean》裏解釋道,反射是爲了能把一個類的屬性可視化的展示給用戶,如下圖所示:

通俗的解釋就是:無論是公有還是私有的方法、屬性、構造方法,全都可以用反射進行獲取、進行賦值、調用。聽到這個解釋,是不是感覺反射很強。

正因爲反射的強大,在java世界裏運用的地方有很多,比如:Java類加載和初始化、Java中RTTI、Spring的IOC,。

如此廣泛的運用,只能說反射除了強,用起來肯定很爽。我想起我的同事,IT界的刁民,總是熱衷於反射。

他在講解他是如何運用反射時,嘴角總是壓抑不住的微笑,這種迷戀反射的樣子,像極了愛情。

正所謂:反射一開,誰都不愛。(傲嬌)

下面就看看反射究竟是如何在程序中使用的。

反射的概述和使用

反射的概述

JAVA反射機制是在運行狀態中,
對於任意一個類,都能夠知道這個類的所有屬性和方法;
對於任意一個對象,都能夠調用它的任意一個方法和屬性;
這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

我們知道class文件是在編譯的時候生成的,Class對象是將class文件讀入內存,併爲之創建一個Class對象。

Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的defineClass 方法自動構造的。也就是這不需要我們自己去處理創建,JVM已經幫我們創建好了。

Class類裏面,包含了一個類應有的所有描述,包括:
字段:Field.java
方法:Method.java
構造方法:Constructor.java
等等...

知道了Class類裏面包含了哪些內容之後,再看一下new一個對象的究竟會發生那些過程:

反射的使用

這裏使用一個Animal類來作爲示範,可以看到這個類裏的成員變量、方法、構造方法的訪問修飾符既有public、也有private的。下面就將使用反射獲取不同修飾符修飾的成員變量、方法、構造方法。

package com.shuai.ioc.ref;

public class Animal {

    /**
     * 動物名字
     */
    public String name;

    /**
     * 動物年齡
     */
    protected int age;

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /**
     * 默認的構造方法
     *
     * @param name
     */
    Animal(String name) {
        System.out.println("執行了" + "默認的構造方法 " + name);
    }

    /**
     * 無參構造方法
     */
    public Animal() {
        System.out.println("執行了" + "無參構造方法 ");
    }

    /**
     * 有一個參數的構造方法
     *
     * @param name
     */
    public Animal(char name) {
        System.out.println("執行了" + "有一個參數的構造方法 name:" + name);

    }

    /**
     * 有多個參數的構造方法
     *
     * @param name
     * @param age
     */
    public Animal(String name, int age) {
        System.out.println("執行了" + "有多個參數的構造方法 name:" + name + "age:" + age);
    }

    /**
     * protected的構造方法
     *
     * @param n
     */
    protected Animal(boolean n) {
        System.out.println("執行了" + "受保護的構造方法 n:" + n);
    }

    /**
     * 私有構造方法
     *
     * @param age
     */
    private Animal(int age) {
        System.out.println("執行了" + "私有構造方法 age:" + age);
        this.name = "私有構造方法調用成功";
        this.age = age;
    }


    /**
     * 公有方法
     *
     * @param s
     */
    public void public1(String s) {
        System.out.println("調用了" + "公有的方法" + ": public1 , s:" + s);
    }

    /**
     * protected的方法
     */
    protected void protected2() {
        System.out.println("調用了" + "protected的方法" + ": protected2 ");
    }

    /**
     * 友好的方法
     */
    void friendly1() {
        System.out.println("調用了" + "友好的方法" + ": friendly1 ");
    }

    /**
     * 私有方法
     *
     * @param age
     * @return
     */
    private String private1(int age) {
        System.out.println("調用了" + "私有方法" + ": private1 ,age:" + age);
        return age + "";
    }


}

用反射獲取類的構造方法

在Class類中,提供一系列獲取被反射類構造方法的方法。

  • 批量獲取構造方法的方法
    • public Constructor[] getConstructors():所有"公有的"構造方法
    • public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)
  • 獲取單個的方法,並調用
    • public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法
    • public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有;
  • 調用構造方法
    • newInstance(Object... initargs)
package com.shuai.ioc.ref;

import com.shuai.ioc.Book;

import java.lang.reflect.Constructor;

public class ConstructorsTest {

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

        //1.加載Class對象
        Class clazz = Class.forName("com.shuai.ioc.ref.Animal");

        //2.獲取所有公有構造方法
        System.out.println("所有公有構造方法");
        Constructor[] conArray = clazz.getConstructors();
        for (Constructor c : conArray) {
            System.out.println(c);
        }

        // 所有的構造方法,公有、私有都行
        System.out.println("");
        System.out.println("所有的構造方法,包括:私有、受保護、默認、公有");
        conArray = clazz.getDeclaredConstructors();
        for (Constructor c : conArray) {
            System.out.println(c);
        }

        // 獲取公有、無參的構造方法
        System.out.println("");
        System.out.println("獲取公有、無參的構造方法");
        Constructor con = clazz.getConstructor(null);

        System.out.println("con = " + con);
        //調用構造方法
        Object obj = con.newInstance();

        // 獲取私有構造方法
        System.out.println("");
        System.out.println("獲取私有構造方法,並調用");
        con = clazz.getDeclaredConstructor(int.class);
        System.out.println(con);
        //暴力訪問,忽略掉訪問修飾符
        con.setAccessible(true);
        //調用構造方法
        Animal animal = (Animal) con.newInstance(1);
        System.out.println(animal.toString());

    }
}

用反射獲取類的方法

在Class類中,提供一系列獲取被反射類構造方法的方法。

  • 批量的
    • public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)
    • public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
  • 獲取單個的
    • public Method getMethod(String name,Class<?>... parameterTypes)name: 方法名;Class ...:形參的Class類型對象
    • public Method getDeclaredMethod(String name,Class<?>... parameterTypes)obj:要調用方法的對象;args:調用方式時所傳遞的實參;
  • 調用方法
    • public Object invoke(Object obj,Object... args)obj:要調用方法的對象;args:調用方式時所傳遞的實參;
package com.shuai.ioc.ref;

import java.lang.reflect.Method;

public class MethodClassTest {

    public static void main(String[] args) throws Exception {
        //1.獲取Class對象
        Class stuClass = Class.forName("com.shuai.ioc.ref.Animal");


        //2.獲取所有公有方法
        System.out.println("獲取所有 公有 方法");
        stuClass.getMethods();
        Method[] methodArray = stuClass.getMethods();
        for (Method m : methodArray) {
            System.out.println(m);
        }

        System.out.println();
        System.out.println("獲取所有的方法,包括私有的");
        methodArray = stuClass.getDeclaredMethods();
        for (Method m : methodArray) {
            System.out.println(m);
        }

        System.out.println();
        System.out.println("獲取公有的public1()方法");
        Method m = stuClass.getMethod("public1", String.class);
        System.out.println(m);
        //實例化一個Student對象
        Object obj = stuClass.getConstructor().newInstance();
        m.invoke(obj, "this is name value");


        System.out.println();
        System.out.println("獲取私有的private1()方法");
        m = stuClass.getDeclaredMethod("private1", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
        System.out.println("返回值:" + result);

    }
}

用反射獲取類的字段

在Class類中,提供一系列獲取被反射類構造方法的方法。

  • 批量的
    • Field[] getFields():獲取所有的"公有字段"
    • Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有;
  • 獲取單個的
    • public Field getField(String fieldName):獲取某個"公有的"字段;
    • public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的)
  • 設置字段的值
    • public void set(Object obj,Object value)obj:要設置的字段所在的對象;value:要爲字段設置的值;
package com.shuai.ioc.ref;

import java.lang.reflect.Field;

public class FieldsTest {

    public static void main(String[] args) throws Exception {
        //1.獲取Class對象
        Class animalClass = Class.forName("com.shuai.ioc.ref.Animal");
        //2.獲取字段
        System.out.println("獲取所有公有的字段");
        Field[] fieldArray = animalClass.getFields();
        for (Field f : fieldArray) {
            System.out.println(f);
        }

        System.out.println();
        System.out.println("獲取所有的字段(包括私有、受保護、默認的)");
        fieldArray = animalClass.getDeclaredFields();
        for (Field f : fieldArray) {
            System.out.println(f);
        }

        System.out.println();
        System.out.println("獲取公有字段並調用");
        Field f = animalClass.getField("name");
        System.out.println(f);
        //獲取一個對象
        Object obj = animalClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();
        //爲字段設置值
        f.set(obj, "dog");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
        //驗證
        Animal stu = (Animal) obj;
        System.out.println("驗證name:" + stu.name);

        System.out.println();
        System.out.println("獲取私有字段並調用");
        f = animalClass.getDeclaredField("name");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(obj, "this is name value");
        System.out.println("驗證name:" + stu);

    }
}

反射越過泛型檢查

編寫代碼時,如果我們設置容器list爲String類型,在調用add方法插入數據時入參傳了其他類型,編譯時會無法成功,但是通過反射卻可以執行,實例代碼:

package com.shuai.ioc.ref;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/*
 * 通過反射越過泛型檢查
 *
 */
public class IgnoreType {
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        list.add("one");
        //反射獲取list對象
        Class listClass = list.getClass();
        // 調用list對象的add方法
        Method m = listClass.getMethod("add", Object.class);
        m.invoke(list, 100);
        //輸出驗證
        for (Object obj : list) {
            System.out.println(obj);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章