JAVA基礎(chǔ)系列(十一)反射

通過(guò)class文件對(duì)象,去使用該文件中的成員變量Field,構(gòu)造方法Constructor,成員方法Method。

1.獲取class文件對(duì)象

獲取class文件對(duì)象的方式:
1:Object類的getClass()方法
2:數(shù)據(jù)類型的靜態(tài)屬性class
3:Class.forName(String className);

** 注意:**在開(kāi)發(fā)是我們一般用第三種,因?yàn)榈谌N是一個(gè)字符串,而不是一個(gè)具體的類名。這樣我們就可以把這樣的字符串配置到配置文件中。

** Demo:輔助類Person**

package com.hust.jianshu.reflectDemos.acamy;

public class Person {
    private String name;
    int age;
    public String address;

    public Person() {
    }

    private Person(String name) {
        this.name = name;
    }

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public void show() {
        System.out.println("show");
    }

    public void method(String s) {
        System.out.println("method " + s);
    }

    public String getString(String s, int i) {
        return s + "---" + i;
    }

    private void function() {
        System.out.println("function");
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address
                + "]";
    }

}

** Demo:獲取class文件對(duì)象的幾種方式**

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1
        Person p = new Person();
        Class c = p.getClass();

        Person p2 = new Person();
        Class c2 = p2.getClass();

        System.out.println(p == p2);// false
        System.out.println(c == c2);// true

        // 方式2
        Class c3 = Person.class;
        // int.class;
        // String.class;
        System.out.println(c == c3);//true

        // 方式3
        // ClassNotFoundException
        Class c4 = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");
        System.out.println(c == c4);//true
    }
}

2.獲取構(gòu)造方法

通過(guò)反射既可以獲取公有構(gòu)造方法,也可以暴力訪問(wèn)私有構(gòu)造方法。同時(shí)可以單個(gè)獲取,也可以批量獲取。

** Demo:通過(guò)反射獲取構(gòu)造方法并使用**

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節(jié)碼文件對(duì)象
        Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");

        // 獲取多個(gè)構(gòu)造方法
        // public Constructor[] getConstructors():所有公共構(gòu)造方法
        // public Constructor[] getDeclaredConstructors():所有構(gòu)造方法
        Constructor[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        // 獲取單個(gè)構(gòu)造方法
        // 獲取無(wú)參構(gòu)造方法對(duì)象
        // public Constructor<T> getConstructor(Class<?>... parameterTypes)
        // 參數(shù)表示的是:你要獲取的構(gòu)造方法的構(gòu)造參數(shù)個(gè)數(shù)及數(shù)據(jù)類型的class字節(jié)碼文件對(duì)象
        Constructor con = c.getConstructor();// 返回的是構(gòu)造方法對(duì)象
        // public T newInstance(Object... initargs)
        // 使用此 Constructor 對(duì)象表示的構(gòu)造方法來(lái)創(chuàng)建該構(gòu)造方法的聲明類的新實(shí)例,并用指定的初始化參數(shù)初始化該實(shí)例。
        Object obj = con.newInstance();
        System.out.println(obj);

        // 獲取帶參構(gòu)造方法對(duì)象
        // public Constructor<T> getConstructor(Class<?>... parameterTypes)
        Constructor con1 = c.getConstructor(String.class, int.class,
                String.class);
        // 通過(guò)帶參構(gòu)造方法對(duì)象創(chuàng)建對(duì)象
        // public T newInstance(Object... initargs)
        Object obj1 = con1.newInstance("林青霞", 27, "北京");
        System.out.println(obj1);

        // 獲取私有構(gòu)造方法對(duì)象
        // NoSuchMethodException:每個(gè)這個(gè)方法異常
        // 原因是一開(kāi)始我們使用的方法只能獲取公共的,下面這種方式就可以了。
        Constructor con2 = c.getDeclaredConstructor(String.class);
        // 用該私有構(gòu)造方法創(chuàng)建對(duì)象
        // IllegalAccessException:非法的訪問(wèn)異常。
        // 暴力訪問(wèn)
        con2.setAccessible(true);// 值為true則指示反射的對(duì)象在使用時(shí)應(yīng)該取消Java語(yǔ)言訪問(wèn)檢查。
        Object obj2 = con2.newInstance("風(fēng)清揚(yáng)");
        System.out.println(obj2);
    }
}

3.獲取成員變量

>通過(guò)反射可以獲取所有類型的成員變量,并且在創(chuàng)建對(duì)象后可以對(duì)獲取的成員變量設(shè)置值

** Demo:通過(guò)反射獲取成員變量并使用**

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節(jié)碼文件對(duì)象
        Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");

        // 獲取多個(gè)的成員變量
        // Field[] fields = c.getFields();所有公共成員變量
        Field[] fields = c.getDeclaredFields();// 所有成員變量
        for (Field field : fields) {
            System.out.println(field);
        }

        // 通過(guò)無(wú)參構(gòu)造方法創(chuàng)建對(duì)象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);

        // 獲取單個(gè)的成員變量
        // 獲取address并對(duì)其賦值
        Field addressField = c.getField("address");
        // public void set(Object obj,Object value)
        // 將指定對(duì)象變量上此 Field 對(duì)象表示的字段設(shè)置為指定的新值。
        addressField.set(obj, "北京"); // 給obj對(duì)象的addressField字段設(shè)置值為"北京"
        System.out.println(obj);

        // 獲取name并對(duì)其賦值
        // NoSuchFieldException
        Field nameField = c.getDeclaredField("name");
        // IllegalAccessException
        nameField.setAccessible(true);
        nameField.set(obj, "林青霞");
        System.out.println(obj);

        // 獲取age并對(duì)其賦值
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj, 27);
        System.out.println(obj);
    }
}

4.獲取成員方法

通過(guò)反射可以獲取對(duì)象的所有類型成員變量,并調(diào)用

** Demo:通過(guò)反射獲取成員方法并使用**

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節(jié)碼文件對(duì)象
        Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");

        // 獲取所有的方法
        // Method[] methods = c.getMethods(); // 獲取自己的包括父親的公共方法
        Method[] methods = c.getDeclaredMethods(); // 獲取自己的所有的方法
        for (Method method : methods) {
            System.out.println(method);
        }

        Constructor con = c.getConstructor();
        Object obj = con.newInstance();     

        // 獲取單個(gè)方法并使用
        // public void show()
        // public Method getMethod(String name,Class<?>... parameterTypes)
        // 第一個(gè)參數(shù)表示的方法名,第二個(gè)參數(shù)表示的是方法的參數(shù)的class類型
        Method m1 = c.getMethod("show");
        // public Object invoke(Object obj,Object... args)
        // 返回值是Object接收,第一個(gè)參數(shù)表示對(duì)象是誰(shuí),第二參數(shù)表示調(diào)用該方法的實(shí)際參數(shù)
        m1.invoke(obj); // 調(diào)用obj對(duì)象的m1方法

        System.out.println("----------");
        // public void method(String s)
        Method m2 = c.getMethod("method", String.class);
        m2.invoke(obj, "hello");
        System.out.println("----------");

        // public String getString(String s, int i)
        Method m3 = c.getMethod("getString", String.class, int.class);
        Object objString = m3.invoke(obj, "hello", 100);
        System.out.println(objString);
        // String s = (String)m3.invoke(obj, "hello",100);
        // System.out.println(s);
        System.out.println("----------");

        // private void function()
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }
}

5.反射應(yīng)用舉例

5.1 通過(guò)反射越過(guò)泛型檢查

集合里面的泛型是給編繹器看的,在編繹成字節(jié)碼后已經(jīng)去掉了該類型,變成時(shí)論的Object類型,可以通過(guò)反編繹工具驗(yàn)證一下。正是由于集合里面的泛型有這種機(jī)制,我們才可以利用反射得到字節(jié)碼文件,越過(guò)泛型檢查

** Demo:**在ArrayList<Integer>集合中添加一個(gè)字符串?dāng)?shù)據(jù)

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ArrayListDemo {
    public static void main(String[] args) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        // 創(chuàng)建集合對(duì)象
        ArrayList<Integer> array = new ArrayList<Integer>();

        // array.add("hello");
        array.add(10);

        Class c = array.getClass(); // 集合ArrayList的class文件對(duì)象
        Method m = c.getMethod("add", Object.class);

        m.invoke(array, "hello"); // 調(diào)用array的add方法,傳入的值是hello
        m.invoke(array, "world");
        m.invoke(array, "java");

        System.out.println(array);
    }
}

5.2 反射工具類

** Demo: 反射工具類**

import java.lang.reflect.Field;

public class ToolDemo {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException {
        Person p = new Person();
        Tool t = new Tool();
        t.setProperty(p, "name", "林青霞");
        t.setProperty(p, "age", 27);
        System.out.println(p);
        System.out.println("-----------");

        Dog d = new Dog();

        t.setProperty(d, "sex", '男');
        t.setProperty(d, "price", 12.34f);

        System.out.println(d);
    }
}

class Tool {
    public void setProperty(Object obj, String propertyName, Object value)
            throws NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        // 根據(jù)對(duì)象獲取字節(jié)碼文件對(duì)象
        Class c = obj.getClass();
        // 獲取該對(duì)象的propertyName成員變量
        Field field = c.getDeclaredField(propertyName);
        // 取消訪問(wèn)檢查
        field.setAccessible(true);
        // 給對(duì)象的成員變量賦值為指定的值
        field.set(obj, value);
    }
}

class Dog {
    char sex;
    float price;

    @Override
    public String toString() {
        return sex + "---" + price;
    }
}

class Person {
    private String name;
    public int age;

    @Override
    public String toString() {
        return name + "---" + age;
    }

6.動(dòng)態(tài)代理

代理:本來(lái)應(yīng)該自己做的事情,卻請(qǐng)了別人來(lái)做,被請(qǐng)的人就是代理對(duì)象。
舉例:春季回家買票讓人代買
動(dòng)態(tài)代理:在程序運(yùn)行過(guò)程中產(chǎn)生的這個(gè)對(duì)象,動(dòng)態(tài)代理其實(shí)就是通過(guò)反射來(lái)生成一個(gè)代理

** Demo:反射實(shí)現(xiàn)動(dòng)態(tài)代理類**

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        UserDao ud = new UserDaoImpl();
        System.out.println(ud.getClass().getName());// ...UserDaoImpl
        ud.add();
        ud.delete();
        ud.update();
        ud.find();
        System.out.println("-----------");
        // 我們要?jiǎng)?chuàng)建一個(gè)動(dòng)態(tài)代理對(duì)象
        // Proxy類中有一個(gè)方法可以創(chuàng)建動(dòng)態(tài)代理對(duì)象

        // 我準(zhǔn)備對(duì)ud對(duì)象做一個(gè)代理對(duì)象
        MyInvocationHandler handler = new MyInvocationHandler(ud);

        // public static Object newProxyInstance(ClassLoader loader,Class<?>[]
        // interfaces,InvocationHandler h)
        // ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
        // Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
        // InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上

        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
                .getClassLoader(), ud.getClass().getInterfaces(), handler);

        // 創(chuàng)建的代理對(duì)象是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)對(duì)象,它并不是我們的InvocationHandler類型,
        // 也不是我們定義的那組接口的類型,而是在運(yùn)行是動(dòng)態(tài)生成的一個(gè)對(duì)象,并且命名方式都是這樣的形式,
        // 以$開(kāi)頭,proxy為中,最后一個(gè)數(shù)字表示對(duì)象的標(biāo)號(hào)。
        System.out.println(proxy.getClass().getName());// ...$Proxy0

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.find();
    }
}

// 每一個(gè)動(dòng)態(tài)代理類都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口,
// 并且每個(gè)代理類的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,
// 當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,
// 這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的invoke 方法來(lái)進(jìn)行調(diào)用。
class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目標(biāo)對(duì)象

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    // proxy:代表動(dòng)態(tài)代理對(duì)象
    // method:代表正在執(zhí)行的方法
    // args:代表調(diào)用目標(biāo)方法時(shí)傳入的實(shí)參
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("權(quán)限校驗(yàn)");
        Object result = method.invoke(target, args);
        System.out.println("日志記錄");
        return result; // 返回的是代理對(duì)象
    }
}

class UserDaoImpl implements UserDao {

    @Override
    public void add() {
        System.out.println("添加功能");
    }

    @Override
    public void delete() {
        System.out.println("刪除功能");
    }

    @Override
    public void update() {
        System.out.println("修改功能");
    }

    @Override
    public void find() {
        System.out.println("查找功能");
    }

}

interface UserDao {
    public abstract void add();

    public abstract void delete();

    public abstract void update();

    public abstract void find();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,973評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,767評(píng)論 18 399
  • 高新技術(shù)的重要性 這里的高新技術(shù)指的是Java基礎(chǔ)中的知識(shí),比如:反射、注解、代理、線程池、依賴注入等等。市面上的...
    龐哈哈哈12138閱讀 2,016評(píng)論 1 19
  • 弟弟蹭蹭蹭下樓來(lái),我在門(mén)口散步,問(wèn)他干嘛去。 他晃晃手中的釣魚(yú)竿,“你猜。” “哪里還有魚(yú)可釣?”門(mén)前兩條河十幾年...
    素素_sky閱讀 236評(píng)論 0 0
  • 好久不看香港電影,雖然香港的電影和TVB劇圈住了整個(gè)少年不知事的歲月…… 而今,香港電影業(yè)已漸趨衰敗難有佳作,TV...
    TKJ閱讀 1,231評(píng)論 0 10