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

反射的發展歷史

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

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

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

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

通俗的解釋就是:不管是公有仍是私有的方法、屬性、構造方法,全均可以用反射進行獲取、進行賦值、調用。聽到這個解釋,是否是感受反射很強。工具

正由於反射的強大,在java世界裏運用的地方有不少,好比:Java類加載和初始化、Java中RTTI、Spring的IOC,。this

如此普遍的運用,只能說反射除了強,用起來確定很爽。我想起個人同事,IT界的刁民,老是熱衷於反射。code

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

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

下面就看看反射到底是如何在程序中使用的。blog

反射的概述和使用

反射的概述

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

歡迎關注公衆號:java之旅

相關文章
相關標籤/搜索