最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java高級-反射&動態(tài)代理詳解

 更新時間:2025年03月13日 16:21:40   作者:L_?。?!  
這篇文章主要介紹了Java高級-反射&動態(tài)代理詳解,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

1. 反射

1.1 反射的概述

專業(yè)的解釋(了解一下):

  • 是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
  • 對于任意一個對象,都能夠調(diào)用它的任意屬性和方法;
  • 這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為Java語言的反射機(jī)制。

通俗的理解:(掌握)

利用反射創(chuàng)建的對象可以無視修飾符調(diào)用類里面的內(nèi)容

可以跟配置文件結(jié)合起來使用,把要創(chuàng)建的對象信息和方法寫在配置文件中。

  • 讀取到什么類,就創(chuàng)建什么類的對象
  • 讀取到什么方法,就調(diào)用什么方法
  • 此時當(dāng)需求變更的時候不需要修改代碼,只要修改配置文件即可。

1.2 學(xué)習(xí)反射到底學(xué)什么?

反射都是從class字節(jié)碼文件中獲取的內(nèi)容。(前提)

  • 如何獲取class字節(jié)碼文件的對象
  • 利用反射如何獲取構(gòu)造方法(創(chuàng)建對象)
  • 利用反射如何獲取成員變量(賦值,獲取值)
  • 利用反射如何獲取成員方法(運(yùn)行)

1.3 獲取字節(jié)碼文件對象的三種方式

  • Class這個類里面的靜態(tài)方法forName(“全類名”)(最常用)
  • 通過class屬性獲取
  • 通過對象獲取字節(jié)碼文件對象

代碼示例:

//1.Class這個類里面的靜態(tài)方法forName
//Class.forName("類的全類名"): 全類名 = 包名 + 類名
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
//源代碼階段獲取 --- 先把Student加載到內(nèi)存中,再獲取字節(jié)碼文件的對象
//clazz 就表示Student這個類的字節(jié)碼文件對象。
//就是當(dāng)Student.class這個文件加載到內(nèi)存之后,產(chǎn)生的字節(jié)碼文件對象
?
?
//2.通過class屬性獲取
//類名.class
Class clazz2 = Student.class;
?
//因為class文件在硬盤中是唯一的,所以,當(dāng)這個文件加載到內(nèi)存之后產(chǎn)生的對象也是唯一的
System.out.println(clazz1 == clazz2);//true
?
?
//3.通過Student對象獲取字節(jié)碼文件對象
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz2 == clazz3);//true

1.4 字節(jié)碼文件和字節(jié)碼文件對象

  • java文件:就是我們自己編寫的java代碼。
  • 字節(jié)碼文件:就是通過java文件編譯之后的class文件(是在硬盤上真實存在的,用眼睛能看到的)
  • 字節(jié)碼文件對象:當(dāng)class文件加載到內(nèi)存之后,虛擬機(jī)自動創(chuàng)建出來的對象。
  • 這個對象里面至少包含了:構(gòu)造方法,成員變量,成員方法。
  • 而我們的反射獲取的是什么?字節(jié)碼文件對象,這個對象在內(nèi)存中是唯一的。

1.5 獲取構(gòu)造方法

規(guī)則:

  • get表示獲取
  • Declared表示私有
  • 最后的s表示所有,復(fù)數(shù)形式
  • 如果當(dāng)前獲取到的是私有的,必須要臨時修改訪問權(quán)限,否則無法使用
方法名說明
Constructor<?>[] getConstructors()獲得所有的構(gòu)造(只能public修飾)
Constructor<?>[] getDeclaredConstructors()獲得所有的構(gòu)造(包含private修飾)
Constructor<T> getConstructor(Class<?>... parameterTypes)獲取指定構(gòu)造(只能public修飾)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)獲取指定構(gòu)造(包含private修飾)

代碼示例:

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.獲得整體(class字節(jié)碼文件對象)
        Class clazz = Class.forName("com.itheima.reflectdemo.Student");
?
?
        //2.獲取構(gòu)造方法對象
        //獲取所有構(gòu)造方法(public)
        Constructor[] constructors1 = clazz.getConstructors();
        for (Constructor constructor : constructors1) {
            System.out.println(constructor);
        }
?
        System.out.println("=======================");
?
        //獲取所有構(gòu)造(帶私有的)
        Constructor[] constructors2 = clazz.getDeclaredConstructors();
?
        for (Constructor constructor : constructors2) {
            System.out.println(constructor);
        }
        System.out.println("=======================");
?
        //獲取指定的空參構(gòu)造
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);
?
        Constructor con2 = clazz.getConstructor(String.class,int.class);
        System.out.println(con2);
?
        System.out.println("=======================");
        //獲取指定的構(gòu)造(所有構(gòu)造都可以獲取到,包括public包括private)
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);
        //了解 System.out.println(con3 == con1);
        //每一次獲取構(gòu)造方法對象的時候,都會新new一個。
?
        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
    }
}

1.6 獲取構(gòu)造方法并創(chuàng)建對象

涉及到的方法:newInstance

代碼示例:

//首先要有一個javabean類
public class Student {
    private String name;
?
    private int age;
?
?
    public Student() {
?
    }
?
    public Student(String name) {
        this.name = name;
    }
?
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
?
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    /**
     * 獲取
     * @return age
     */
    public int getAge() {
        return age;
    }
?
    /**
     * 設(shè)置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
?
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
?
?
?
//測試類中的代碼:
//需求1:
//獲取空參,并創(chuàng)建對象
?
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取空參的構(gòu)造方法
Constructor con = clazz.getConstructor();
//3.利用空參構(gòu)造方法創(chuàng)建對象
Student stu = (Student) con.newInstance();
System.out.println(stu);
?
?
System.out.println("=============================================");
?
?
//測試類中的代碼:
//需求2:
//獲取帶參構(gòu)造,并創(chuàng)建對象
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取有參構(gòu)造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.臨時修改構(gòu)造方法的訪問權(quán)限(暴力反射)
con.setAccessible(true);
//4.直接創(chuàng)建對象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);

1.7 獲取成員變量

規(guī)則:

  • get表示獲取
  • Declared表示私有
  • 最后的s表示所有,復(fù)數(shù)形式
  • 如果當(dāng)前獲取到的是私有的,必須要臨時修改訪問權(quán)限,否則無法使用

方法名:

方法名說明
Field[] getFields()返回所有成員變量對象的數(shù)組(只能拿public的)
Field[] getDeclaredFields()返回所有成員變量對象的數(shù)組,存在就能拿到
Field getField(String name)返回單個成員變量對象(只能拿public的)
Field getDeclaredField(String name)返回單個成員變量對象,存在就能拿到

代碼示例:

public class ReflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //獲取成員變量對象
?
        //1.獲取class對象
        Class clazz = Class.forName("com.itheima.reflectdemo.Student");
?
        //2.獲取成員變量的對象(Field對象)只能獲取public修飾的
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
?
        System.out.println("===============================");
?
        //獲取成員變量的對象(public + private)
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }
?
        System.out.println("===============================");
        //獲得單個成員變量對象
        //如果獲取的屬性是不存在的,那么會報異常
        //Field field3 = clazz.getField("aaa");
        //System.out.println(field3);//NoSuchFieldException
?
        Field field4 = clazz.getField("gender");
        System.out.println(field4);
?
        System.out.println("===============================");
        //獲取單個成員變量(私有)
        Field field5 = clazz.getDeclaredField("name");
        System.out.println(field5);
?
    }
}
?
?
?
public class Student {
    private String name;
?
    private int age;
?
    public String gender;
?
    public String address;
?
?
    public Student() {
    }
?
    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
?
?
    public Student(String name, int age, String gender, String address) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.address = address;
    }
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    /**
     * 獲取
     * @return age
     */
    public int getAge() {
        return age;
    }
?
    /**
     * 設(shè)置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
?
    /**
     * 獲取
     * @return gender
     */
    public String getGender() {
        return gender;
    }
?
    /**
     * 設(shè)置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }
?
    /**
     * 獲取
     * @return address
     */
    public String getAddress() {
        return address;
    }
?
    /**
     * 設(shè)置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }
?
    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
    }
}

1.8 獲取成員變量并獲取值和修改值

方法說明
void set(Object obj, Object value)賦值
Object get(Object obj)獲取值

代碼示例:

public class ReflectDemo5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Student s = new Student("zhangsan",23,"廣州");
        Student ss = new Student("lisi",24,"北京");
?
        //需求:
        //利用反射獲取成員變量并獲取值和修改值
?
        //1.獲取class對象
        Class clazz = Class.forName("com.itheima.reflectdemo.Student");
?
        //2.獲取name成員變量
        //field就表示name這個屬性的對象
        Field field = clazz.getDeclaredField("name");
        //臨時修飾他的訪問權(quán)限
        field.setAccessible(true);
?
        //3.設(shè)置(修改)name的值
        //參數(shù)一:表示要修改哪個對象的name?
        //參數(shù)二:表示要修改為多少?
        field.set(s,"wangwu");
?
        //3.獲取name的值
        //表示我要獲取這個對象的name的值
        String result = (String)field.get(s);
?
        //4.打印結(jié)果
        System.out.println(result);
?
        System.out.println(s);
        System.out.println(ss);
?
    }
}
?
?
public class Student {
    private String name;
    private int age;
    public String gender;
    public String address;
?
?
    public Student() {
    }
?
    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
?
?
    public Student(String name, int age, String gender, String address) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.address = address;
    }
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    /**
     * 獲取
     * @return age
     */
    public int getAge() {
        return age;
    }
?
    /**
     * 設(shè)置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
?
    /**
     * 獲取
     * @return gender
     */
    public String getGender() {
        return gender;
    }
?
    /**
     * 設(shè)置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }
?
    /**
     * 獲取
     * @return address
     */
    public String getAddress() {
        return address;
    }
?
    /**
     * 設(shè)置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }
?
    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
    }
}

1.9 獲取成員方法

規(guī)則:

  • get表示獲取
  • Declared表示私有
  • 最后的s表示所有,復(fù)數(shù)形式
  • 如果當(dāng)前獲取到的是私有的,必須要臨時修改訪問權(quán)限,否則無法使用
方法名說明
Method[] getMethods()返回所有成員方法對象的數(shù)組(只能拿public的)
Method[] getDeclaredMethods()返回所有成員方法對象的數(shù)組,存在就能拿到
Method getMethod(String name, Class<?>... parameterTypes)返回單個成員方法對象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)返回單個成員方法對象,存在就能拿到

代碼示例:

public class ReflectDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.獲取class對象
        Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");
?
?
        //2.獲取方法
        //getMethods可以獲取父類中public修飾的方法
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }
?
        System.out.println("===========================");
        //獲取所有的方法(包含私有)
        //但是只能獲取自己類中的方法
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }
?
        System.out.println("===========================");
        //獲取指定的方法(空參)
        Method method3 = clazz.getMethod("sleep");
        System.out.println(method3);
?
        Method method4 = clazz.getMethod("eat",String.class);
        System.out.println(method4);
?
        //獲取指定的私有方法
        Method method5 = clazz.getDeclaredMethod("playGame");
        System.out.println(method5);
    }
}

1.10 獲取成員方法并運(yùn)行

方法

  • Object invoke(Object obj, Object... args) :運(yùn)行方法
  • 參數(shù)一:用obj對象調(diào)用該方法
  • 參數(shù)二:調(diào)用方法的傳遞的參數(shù)(如果沒有就不寫)
  • 返回值:方法的返回值(如果沒有就不寫)

代碼示例:

package com.itheima.a02reflectdemo1;
?
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
?
public class ReflectDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.獲取字節(jié)碼文件對象
        Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
        
        //2.獲取一個對象
        //需要用這個對象去調(diào)用方法
        Student s = new Student();
        
        //3.獲取一個指定的方法
        //參數(shù)一:方法名
        //參數(shù)二:參數(shù)列表,如果沒有可以不寫
        Method eatMethod = clazz.getMethod("eat",String.class);
        
        //運(yùn)行
        //參數(shù)一:表示方法的調(diào)用對象
        //參數(shù)二:方法在運(yùn)行時需要的實際參數(shù)
        //注意點(diǎn):如果方法有返回值,那么需要接收invoke的結(jié)果
        //如果方法沒有返回值,則不需要接收
        String result = (String) eatMethod.invoke(s, "重慶小面");
        System.out.println(result);
?
    }
}
?
?
?
public class Student {
    private String name;
    private int age;
    public String gender;
    public String address;
?
?
    public Student() {
?
    }
?
    public Student(String name) {
        this.name = name;
    }
?
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    /**
     * 獲取
     * @return age
     */
    public int getAge() {
        return age;
    }
?
    /**
     * 設(shè)置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
?
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
?
    private void study(){
        System.out.println("學(xué)生在學(xué)習(xí)");
    }
?
    private void sleep(){
        System.out.println("學(xué)生在睡覺");
    }
?
    public String eat(String something){
        System.out.println("學(xué)生在吃" + something);
        return "學(xué)生已經(jīng)吃完了,非常happy";
    }
}

面試題:

你覺得反射好不好?好,有兩個方向

  • 第一個方向:無視修飾符訪問類中的內(nèi)容。但是這種操作在開發(fā)中一般不用,都是框架底層來用的。
  • 第二個方向:反射可以跟配置文件結(jié)合起來使用,動態(tài)的創(chuàng)建對象,動態(tài)的調(diào)用方法。

1.11 練習(xí)泛型擦除(掌握概念,了解代碼)

理解:(掌握)

集合中的泛型只在java文件中存在,當(dāng)編譯成class文件之后,就沒有泛型了。

代碼示例:(了解)

package com.itheima.reflectdemo;
?
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
?
public class ReflectDemo8 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.創(chuàng)建集合對象
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
//        list.add("aaa");
?
        //2.利用反射運(yùn)行add方法去添加字符串
        //因為反射使用的是class字節(jié)碼文件
?
        //獲取class對象
        Class clazz = list.getClass();
?
        //獲取add方法對象
        Method method = clazz.getMethod("add", Object.class);
?
        //運(yùn)行方法
        method.invoke(list,"aaa");
?
        //打印集合
        System.out.println(list);
    }
}

1.12 練習(xí):修改字符串的內(nèi)容(掌握概念,了解代碼)

在這個練習(xí)中,我需要你掌握的是字符串不能修改的真正原因。

字符串,在底層是一個byte類型的字節(jié)數(shù)組,名字叫做value

private final byte[] value;

真正不能被修改的原因:final和private

  • final修飾value表示value記錄的地址值不能修改。
  • private修飾value而且沒有對外提供getvalue和setvalue的方法。所以,在外界不能獲取或修改value記錄的地址值。

如果要強(qiáng)行修改可以用反射:

代碼示例:(了解)

String s = "abc";
String ss = "abc";
// private final byte[] value= {97,98,99};
// 沒有對外提供getvalue和setvalue的方法,不能修改value記錄的地址值
// 如果我們利用反射獲取了value的地址值。
// 也是可以修改的,final修飾的value
// 真正不可變的value數(shù)組的地址值,里面的內(nèi)容利用反射還是可以修改的,比較危險
?
//1.獲取class對象
Class clazz = s.getClass();
?
//2.獲取value成員變量(private)
Field field = clazz.getDeclaredField("value");
//但是這種操作非常危險
//JDK高版本已經(jīng)屏蔽了這種操作,低版本還是可以的
//臨時修改權(quán)限
field.setAccessible(true);
?
//3.獲取value記錄的地址值
byte[] bytes = (byte[]) field.get(s);
bytes[0] = 100;
?
System.out.println(s);//dbc
System.out.println(ss);//dbc

1.13 練習(xí),反射和配置文件結(jié)合動態(tài)獲取的練習(xí)(重點(diǎn))

需求: 利用反射根據(jù)文件中的不同類名和方法名,創(chuàng)建不同的對象并調(diào)用方法。

分析:

  • ①通過Properties加載配置文件
  • ②得到類名和方法名
  • ③通過類名反射得到Class對象
  • ④通過Class對象創(chuàng)建一個對象
  • ⑤通過Class對象得到方法
  • ⑥調(diào)用方法

代碼示例:

public class ReflectDemo9 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.讀取配置文件的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("day14-code\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);
?
        String classname = prop.get("classname") + "";
        String methodname = prop.get("methodname") + "";
?
        //2.獲取字節(jié)碼文件對象
        Class clazz = Class.forName(classname);
?
        //3.要先創(chuàng)建這個類的對象
        Constructor con = clazz.getDeclaredConstructor();
        con.setAccessible(true);
        Object o = con.newInstance();
        System.out.println(o);
?
        //4.獲取方法的對象
        Method method = clazz.getDeclaredMethod(methodname);
        method.setAccessible(true);
?
        //5.運(yùn)行方法
        method.invoke(o);
?
?
    }
}
?
配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep

1.14 利用反射保存對象中的信息(重點(diǎn))

public class MyReflectDemo {
    public static void main(String[] args) throws IllegalAccessException, IOException {
    /*
        對于任意一個對象,都可以把對象所有的字段名和值,保存到文件中去
    */
       Student s = new Student("小A",23,'女',167.5,"睡覺");
       Teacher t = new Teacher("播妞",10000);
       saveObject(s);
    }
?
    //把對象里面所有的成員變量名和值保存到本地文件中
    public static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1.獲取字節(jié)碼文件的對象
        Class clazz = obj.getClass();
        //2. 創(chuàng)建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\a.txt"));
        //3. 獲取所有的成員變量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //獲取成員變量的名字
            String name = field.getName();
            //獲取成員變量的值
            Object value = field.get(obj);
            //寫出數(shù)據(jù)
            bw.write(name + "=" + value);
            bw.newLine();
        }
?
        bw.close();
?
    }
}
public class Student {
    private String name;
    private int age;
    private char gender;
    private double height;
    private String hobby;
?
    public Student() {
    }
?
    public Student(String name, int age, char gender, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.hobby = hobby;
    }
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    /**
     * 獲取
     * @return age
     */
    public int getAge() {
        return age;
    }
?
    /**
     * 設(shè)置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
?
    /**
     * 獲取
     * @return gender
     */
    public char getGender() {
        return gender;
    }
?
    /**
     * 設(shè)置
     * @param gender
     */
    public void setGender(char gender) {
        this.gender = gender;
    }
?
    /**
     * 獲取
     * @return height
     */
    public double getHeight() {
        return height;
    }
?
    /**
     * 設(shè)置
     * @param height
     */
    public void setHeight(double height) {
        this.height = height;
    }
?
    /**
     * 獲取
     * @return hobby
     */
    public String getHobby() {
        return hobby;
    }
?
    /**
     * 設(shè)置
     * @param hobby
     */
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
?
    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
    }
}
public class Teacher {
    private String name;
    private double salary;
?
    public Teacher() {
    }
?
    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    /**
     * 獲取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }
?
    /**
     * 設(shè)置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }
?
    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}
?

2. 動態(tài)代理

2.1 好處

無侵入式的給方法增強(qiáng)功能

2.2 動態(tài)代理三要素

  • 1,真正干活的對象
  • 2,代理對象
  • 3,利用代理調(diào)用方法

切記一點(diǎn):代理可以增強(qiáng)或者攔截的方法都在接口中,接口需要寫在newProxyInstance的第二個參數(shù)里。

2.3 代碼實現(xiàn)

public class Test {
    public static void main(String[] args) {
    /*
        需求:
            外面的人想要大明星唱一首歌
             1. 獲取代理的對象
                代理對象 = ProxyUtil.createProxy(大明星的對象);
             2. 再調(diào)用代理的唱歌方法
                代理對象.唱歌的方法("只因你太美");
     */
        //1. 獲取代理的對象
        BigStar bigStar = new BigStar("雞哥");
        Star proxy = ProxyUtil.createProxy(bigStar);
?
        //2. 調(diào)用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}
/*
*
* 類的作用:
*       創(chuàng)建一個代理
*
* */
public class ProxyUtil {
    /*
    *
    * 方法的作用:
    *       給一個明星的對象,創(chuàng)建一個代理
    *
    *  形參:
    *       被代理的明星對象
    *
    *  返回值:
    *       給明星創(chuàng)建的代理
    *
    *
    *
    * 需求:
    *   外面的人想要大明星唱一首歌
    *   1. 獲取代理的對象
    *      代理對象 = ProxyUtil.createProxy(大明星的對象);
    *   2. 再調(diào)用代理的唱歌方法
    *      代理對象.唱歌的方法("只因你太美");
    * */
    public static Star createProxy(BigStar bigStar){
       /* java.lang.reflect.Proxy類:提供了為對象產(chǎn)生代理對象的方法:
?
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        參數(shù)一:用于指定用哪個類加載器,去加載生成的代理類
        參數(shù)二:指定接口,這些接口用于指定生成的代理長什么,也就是有哪些方法
        參數(shù)三:用來指定生成的代理對象要干什么事情*/
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//參數(shù)一:用于指定用哪個類加載器,去加載生成的代理類
                new Class[]{Star.class},//參數(shù)二:指定接口,這些接口用于指定生成的代理長什么,也就是有哪些方法
                //參數(shù)三:用來指定生成的代理對象要干什么事情
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                        * 參數(shù)一:代理的對象
                        * 參數(shù)二:要運(yùn)行的方法 sing
                        * 參數(shù)三:調(diào)用sing方法時,傳遞的實參
                        * */
                        if("sing".equals(method.getName())){
                            System.out.println("準(zhǔn)備話筒,收錢");
                        }else if("dance".equals(method.getName())){
                            System.out.println("準(zhǔn)備場地,收錢");
                        }
                        //去找大明星開始唱歌或者跳舞
                        //代碼的表現(xiàn)形式:調(diào)用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}
public interface Star {
    //我們可以把所有想要被代理的方法定義在接口當(dāng)中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}
public class BigStar implements Star {
    private String name;
?
?
    public BigStar() {
    }
?
    public BigStar(String name) {
        this.name = name;
    }
?
    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "謝謝";
    }
?
    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }
?
    /**
     * 獲取
     * @return name
     */
    public String getName() {
        return name;
    }
?
    /**
     * 設(shè)置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
?
    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}
?

2.4 額外擴(kuò)展

動態(tài)代理,還可以攔截方法(重點(diǎn))

比如:

  • 在這個故事中,經(jīng)濟(jì)人作為代理,如果別人讓邀請大明星去唱歌,打籃球,經(jīng)紀(jì)人就增強(qiáng)功能。
  • 但是如果別人讓大明星去掃廁所,經(jīng)紀(jì)人就要攔截,不會去調(diào)用大明星的方法。
/*
* 類的作用:
*       創(chuàng)建一個代理
* */
public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if("cleanWC".equals(method.getName())){
                            System.out.println("攔截,不調(diào)用大明星的方法");
                            return null;
                        }
                        //如果是其他方法,正常執(zhí)行
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}

2.5 動態(tài)代理的練習(xí)

對add方法進(jìn)行增強(qiáng),對remove方法進(jìn)行攔截,對其他方法不攔截也不增強(qiáng)

public class MyProxyDemo1 {
    public static void main(String[] args) {
        //動態(tài)代碼可以增強(qiáng)也可以攔截
        //1.創(chuàng)建真正干活的人
        ArrayList<String> list = new ArrayList<>();
?
        //2.創(chuàng)建代理對象
        //參數(shù)一:類加載器。當(dāng)前類名.class.getClassLoader()
        //                 找到是誰,把當(dāng)前的類,加載到內(nèi)存中了,我再麻煩他幫我干一件事情,把后面的代理類,也加載到內(nèi)存
?
        //參數(shù)二:是一個數(shù)組,在數(shù)組里面寫接口的字節(jié)碼文件對象。
        //                  如果寫了List,那么表示代理,可以代理List接口里面所有的方法,對這些方法可以增強(qiáng)或者攔截
        //                  但是,一定要寫ArrayList真實實現(xiàn)的接口
        //                  假設(shè)在第二個參數(shù)中,寫了MyInter接口,那么是錯誤的。
        //                  因為ArrayList并沒有實現(xiàn)這個接口,那么就無法對這個接口里面的方法,進(jìn)行增強(qiáng)或攔截
        //參數(shù)三:用來創(chuàng)建代理對象的匿名內(nèi)部類
        List proxyList = (List) Proxy.newProxyInstance(
                //參數(shù)一:類加載器
                MyProxyDemo1.class.getClassLoader(),
                //參數(shù)二:是一個數(shù)組,表示代理對象能代理的方法范圍
                new Class[]{List.class},
                //參數(shù)三:本質(zhì)就是代理對象
                new InvocationHandler() {
                    @Override
                    //invoke方法參數(shù)的意義
                    //參數(shù)一:表示代理對象,一般不用(了解)
                    //參數(shù)二:就是方法名,我們可以對方法名進(jìn)行判斷,是增強(qiáng)還是攔截
                    //參數(shù)三:就是下面第三步調(diào)用方法時,傳遞的參數(shù)。
                    //舉例1:
                    //list.add("阿瑋好帥");
                    //此時參數(shù)二就是add這個方法名
                    //此時參數(shù)三 args[0] 就是 阿瑋好帥
                    //舉例2:
                    //list.set(1, "aaa");
                    //此時參數(shù)二就是set這個方法名
                    //此時參數(shù)三  args[0] 就是 1  args[1]"aaa"
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //對add方法做一個增強(qiáng),統(tǒng)計耗時時間
                        if (method.getName().equals("add")) {
                            long start = System.currentTimeMillis();
                            //調(diào)用集合的方法,真正的添加數(shù)據(jù)
                            method.invoke(list, args);
                            long end = System.currentTimeMillis();
                            System.out.println("耗時時間:" + (end - start));
                            //需要進(jìn)行返回,返回值要跟真正增強(qiáng)或者攔截的方法保持一致
                            return true;
                        }else if(method.getName().equals("remove") && args[0] instanceof Integer){
                            System.out.println("攔截了按照索引刪除的方法");
                            return null;
                        }else if(method.getName().equals("remove")){
                            System.out.println("攔截了按照對象刪除的方法");
                            return false;
                        }else{
                            //如果當(dāng)前調(diào)用的是其他方法,我們既不增強(qiáng),也不攔截
                            method.invoke(list,args);
                            return null;
                        }
                    }
                }
        );
?
        //3.調(diào)用方法
        //如果調(diào)用者是list,就好比繞過了第二步的代碼,直接添加元素
        //如果調(diào)用者是代理對象,此時代理才能幫我們增強(qiáng)或者攔截
?
        //每次調(diào)用方法的時候,都不會直接操作集合
        //而是先調(diào)用代理里面的invoke,在invoke方法中進(jìn)行判斷,可以增強(qiáng)或者攔截
        proxyList.add("aaa");
        proxyList.add("bbb");
        proxyList.add("ccc");
        proxyList.add("ddd");
?
        proxyList.remove(0);
        proxyList.remove("aaa");
?
?
        //打印集合
        System.out.println(list);
    }
}

總結(jié)

什么是反射?

  • 1.反射就是就是一種通過獲取類的字節(jié)碼文件對象的方式進(jìn)而獲取類的構(gòu)造函數(shù),屬性,方法的一種機(jī)制
  • 2.優(yōu)點(diǎn)就是可以幫助我們簡化一些操作,例如簡化配置文件,缺點(diǎn)就是我們原本設(shè)置的私有屬性通過反射也能獲取,不安全

什么是動態(tài)代理?

  • 一種無侵入式的給方法增強(qiáng)功能的機(jī)制

動態(tài)代理三要素?

  • 1.真正干活的對象
  • 2.代理對象
  • 3.利用代理調(diào)用方法

代理對象和真正干活的對象同方法名,使用代理技術(shù)后,當(dāng)調(diào)用原方法時不再調(diào)用真正干活的對象,而是去調(diào)用代理對象,代理對象底層會把真正干活的對象和自己的功能進(jìn)行拼接,然后再去執(zhí)行。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 電腦上安裝多個JDK版本時該如何自由切換(詳細(xì)圖文)

    電腦上安裝多個JDK版本時該如何自由切換(詳細(xì)圖文)

    我們在學(xué)習(xí)的過程中經(jīng)常用到不同的jdk版本,那么如何在一臺電腦上同時安裝多個jdk版本并進(jìn)行切換呢,這篇文章主要給大家介紹了關(guān)于電腦上安裝多個JDK版本時該如何自由切換的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • Spring的IOC容器實例化bean的方式總結(jié)

    Spring的IOC容器實例化bean的方式總結(jié)

    IOC容器實例化bean的三種方式:構(gòu)造方法、靜態(tài)工廠、實例工廠,本文將通過代碼示例給大家詳細(xì)講解一下這三種方式,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-01-01
  • 輕松搞定SpringBoot JPA使用配置過程詳解

    輕松搞定SpringBoot JPA使用配置過程詳解

    Spring Boot是由Pivotal團(tuán)隊提供的全新框架,該框架使用了特定的方式來進(jìn)行配置,它默認(rèn)配置了很多框架的使用方式,就像 Maven整合了所有的Jar包,Spring Boot 整合了所有的框架
    2021-06-06
  • JSONArray在Java中的應(yīng)用操作實例

    JSONArray在Java中的應(yīng)用操作實例

    JSONArray是org.json庫用于處理JSON數(shù)組的類,可將Java對象(Map/List)轉(zhuǎn)換為JSON格式,提供增刪改查等操作,適用于前后端數(shù)據(jù)交互,并涵蓋安全、性能優(yōu)化及最佳實踐,本文給大家介紹JSONArray在Java中的應(yīng)用,感興趣的朋友一起看看吧
    2025-07-07
  • Java ProcessBuilder執(zhí)行多次CMD命令的使用

    Java ProcessBuilder執(zhí)行多次CMD命令的使用

    本文介紹了Java的ProcessBuilder類,該類用于執(zhí)行外部命令,通過ProcessBuilder,我們可以在Java程序中靈活地執(zhí)行多次CMD命令,并控制輸入輸出流以及工作目錄等,感興趣的可以了解一下
    2024-11-11
  • springboot打成jar后獲取classpath下文件失敗的解決方案

    springboot打成jar后獲取classpath下文件失敗的解決方案

    這篇文章主要介紹了使用springboot打成jar后獲取classpath下文件失敗的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Redisson 分布式延時隊列 RedissonDelayedQueue 運(yùn)行流程

    Redisson 分布式延時隊列 RedissonDelayedQueue 運(yùn)行流程

    這篇文章主要介紹了Redisson分布式延時隊列 RedissonDelayedQueue運(yùn)行流程,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • 一文帶你了解Java萬物之基之Object類

    一文帶你了解Java萬物之基之Object類

    Java是一門天然的面向?qū)ο蟮恼Z言。而所有我們手動創(chuàng)造出來的類,都繼承于同一個類,即Object類。本文將通過示例為大家詳細(xì)介紹一下Java中的Object類,需要的可以參考一下
    2022-03-03
  • springboot設(shè)置了server.port但是沒有用,還是8080問題

    springboot設(shè)置了server.port但是沒有用,還是8080問題

    這篇文章主要介紹了springboot設(shè)置了server.port但是沒有用,還是8080問題的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 使用Java讀取本地文件并轉(zhuǎn)換為MultipartFile對象的方法

    使用Java讀取本地文件并轉(zhuǎn)換為MultipartFile對象的方法

    在許多Java Web應(yīng)用中,我們經(jīng)常會遇到將本地文件上傳至服務(wù)器或其他系統(tǒng)的需求,在這種場景下,MultipartFile對象非常常用,用來表示HTTP請求中的文件,在本文中,我將演示如何編寫代碼來讀取本地文件并將其轉(zhuǎn)換為自定義的MultipartFile對象,需要的朋友可以參考下
    2025-08-08

最新評論

定远县| 确山县| 湘潭市| 河源市| 波密县| 武冈市| 忻州市| 溆浦县| 朝阳市| 柳江县| 获嘉县| 兖州市| 新宾| 横山县| 历史| 穆棱市| 松原市| 浦江县| 通城县| 华亭县| 龙海市| 宁乡县| 宁津县| 剑河县| 寿光市| 唐山市| 邵阳市| 琼中| 拉孜县| 炉霍县| 桃园市| 贵定县| 泗阳县| 丁青县| 云梦县| 屏东市| 苏尼特左旗| 泸西县| 靖宇县| 木兰县| 丰县|