Java反射和動態(tài)代理的使用解讀
1、反射
1.1 反射的概述
是在運行狀態(tài)中,不用創(chuàng)建對象就能夠調(diào)用任意一個類的所有屬性和方法;
1.2 反射作用
反射都是從class字節(jié)碼文件中獲取的內(nèi)容。
- 獲取class字節(jié)碼文件的對象
- 利用反射如何獲取構(gòu)造方法(創(chuàng)建對象)
- 利用反射如何獲取成員變量(賦值,獲取值)
- 利用反射如何獲取成員方法(運行)
1.3 獲取字節(jié)碼文件對象的方式
- Class這個類里面的靜態(tài)方法forName(“全類名”)(最常用)
- 通過class屬性獲取,一般更多的是當(dāng)做參數(shù)進行傳遞
- 通過對象獲取字節(jié)碼文件對象,當(dāng)我們已經(jīng)有了這個類的對象時,才可以使用。
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1. 第一種方式
//全類名 : 包名 + 類名
Class clazz1 = Class.forName("com.ya.reflect.demo01.Student");
//2. 第二種方式
Class clazz2 = Student.class;
//3.第三種方式
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);
System.out.println(clazz2 == clazz3);
}
}1.4 字節(jié)碼文件和字節(jié)碼文件對象
- java文件(源代碼階段):就是編寫的java代碼。
- 字節(jié)碼文件(加載階段):就是通過java文件編譯之后的class文件(是在硬盤上真實存在的,用眼睛能看到的)
- 字節(jié)碼文件對象(運行階段):當(dāng)class文件加載到內(nèi)存之后,虛擬機自動創(chuàng)建出來的對象。這個對象里面至少包含了:構(gòu)造方法,成員變量,成員方法。
反射獲取的是字節(jié)碼文件對象,這個對象在內(nèi)存中是唯一的。
1.5 獲取構(gòu)造方法
規(guī)則:
- get表示獲取
- Declared表示私有
- 最后的s表示所有,復(fù)數(shù)形式
- 如果當(dāng)前獲取到的是私有的,必須要臨時修改訪問權(quán)限setAccessible(true),否則無法使用
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.獲取class字節(jié)碼文件對象
Class clazz = Class.forName("com.ya.reflect.demo02.Student");
//2.獲取構(gòu)造方法
// 2.1 返回所有公共構(gòu)造方法對象的數(shù)組
System.out.println("-----返回所有公共構(gòu)造方法對象的數(shù)組----");
Constructor[] con1 = clazz.getConstructors();
for (Constructor con : con1) {
System.out.println(con);
}
// 2.2 返回所有構(gòu)造方法對象的數(shù)組
System.out.println("-------返回所有構(gòu)造方法對象的數(shù)組------");
Constructor[] con2 = clazz.getDeclaredConstructors();
for (Constructor con : con2) {
System.out.println(con);
}
// 2.3 返回所有構(gòu)造方法對象的數(shù)組
System.out.println("-------返回單個公共構(gòu)造方法對象------");
Constructor con3 = clazz.getConstructor(String.class);
System.out.println(con3);
//2.4 獲取指定的空參構(gòu)造
System.out.println("-------獲取指定的空參構(gòu)造------");
Constructor con4 = clazz.getConstructor();
System.out.println(con4);
// 2.5 返回所有構(gòu)造方法對象的數(shù)組
System.out.println("-------返回單個構(gòu)造方法對象------");
Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(con5);
System.out.println("-----獲取構(gòu)造方法的權(quán)限修飾符(返回整數(shù))--------");
// 返回的是整數(shù),public 1,private 2,protected 4,abstract 1024
int modifiers = con5.getModifiers();
System.out.println(modifiers);
System.out.println("-----獲取構(gòu)造方法的參數(shù)--------");
Parameter[] parameters = con5.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
System.out.println("--------創(chuàng)建對象-----------");
//暴力反射:表示臨時取消權(quán)限校驗
con5.setAccessible(true); // 如果這個權(quán)限修飾符是私有的,就要取消權(quán)限校驗
Student stu = (Student) con5.newInstance("張三", 23);
System.out.println(stu);
}
}1.6 獲取構(gòu)造方法并創(chuàng)建對象
public class ReflectDemo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//需求1:獲取空參,并創(chuàng)建對象(所創(chuàng)建的對象的屬性是默認值)
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.ya.reflect.demo02.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 clazz2 = Class.forName("com.ya.reflect.demo02.Student");
//2.獲取有參構(gòu)造方法
Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class);
//3.臨時修改構(gòu)造方法的訪問權(quán)限(暴力反射)
con2.setAccessible(true);
//4.直接創(chuàng)建對象
Student stu2 = (Student) con2.newInstance("zhangsan", 23);
System.out.println(stu2);
}
}1.7 獲取成員變量并獲取值和修改值
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//1.獲取class字節(jié)碼文件的對象
Class clazz = Class.forName("com.ya.reflect.demo03.Student");
//2.獲取所有的成員變量
System.out.println("-----獲取所有的成員變量----");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//3.獲取單個的成員變量
System.out.println("-----獲取單個的成員變量----");
Field fieldName = clazz.getDeclaredField("name");
System.out.println(fieldName);
System.out.println("-----獲取成員變量的數(shù)據(jù)類型----");
Class<?> type = fieldName.getType();
System.out.println(type);
System.out.println("-----獲取成員變量記錄的值----");
Student student = new Student("zhangsan", 23, "男");
fieldName.setAccessible(true);
String value = (String) fieldName.get(student);
System.out.println(value);
System.out.println("-----修改對象里面記錄的值---");
fieldName.set(student,"lisi");
System.out.println(student);
}
}1.8 獲取成員方法
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1. 獲取class字節(jié)碼文件對象
Class clazz = Class.forName("com.ya.reflect.demo04.Student");
//2. 獲取里面所有的方法對象(包含父類中所有的公共方法)
System.out.println("-------獲取里面所有的方法對象(包含父類中所有的公共方法)-----");
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 3. 獲取里面所有的方法對象(不能獲取父類的,但是可以獲取本類中私有的方法)
System.out.println("--獲取里面所有的方法對象(不能獲取父類的,但是可以獲取本類中私有的方法)--");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
// 4.獲取指定的單一方法
System.out.println("-------獲取指定的單一方法-----");
Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
System.out.println(eatMethod);
// 5.獲取方法的修飾符(返回整數(shù))
System.out.println("-------獲取方法的修飾符(返回整數(shù))-----");
int modifiers = eatMethod.getModifiers();
System.out.println(modifiers);
// 6.獲取方法的名字
System.out.println("-------獲取方法的名字-----");
String eatMethodName = eatMethod.getName();
System.out.println(eatMethodName);
// 7.獲取方法的形參
System.out.println("-------獲取方法的形參-----");
Parameter[] parameters = eatMethod.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//8.獲取方法的拋出的異常
System.out.println("-------獲取方法的拋出的異常-----");
Class[] exceptionTypes = eatMethod.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
}
}1.9 獲取成員方法并運行
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1. 獲取class字節(jié)碼文件對象
Class clazz = Class.forName("com.ya.reflect.demo04.Student");
//2. 獲取指定的單一方法
Method eatMethod = clazz.getDeclaredMethod("eat", String.class);
// 3. 方法運行
System.out.println("-------方法運行-----");
Student student = new Student();
eatMethod.setAccessible(true);
String result = (String) eatMethod.invoke(student, "漢堡包");
System.out.println(result);
}
}2. 動態(tài)代理
2.1 動態(tài)代理好處
- 無侵入式的給方法增強功能。
- 調(diào)用者(BitStar)–>代理(ProxyUtil)–>對象(Star)
2.2 動態(tài)代理三要素
- 接口:代理對象,被代理類和代理類都需要實現(xiàn)這個接口(本例是Star)
- 代理類:通過Proxy類動態(tài)生成的代理類(本例是ProxyUtil)
- 被代理類:代理類實例,它會代替被代理對象處理方法調(diào)用(本例是BigStar)
注:代理可以增強或者攔截的方法都在接口中,接口需要寫在newProxyInstance的第二個參數(shù)里。
2.3 動態(tài)代理簡單實現(xiàn)
//代理對象
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 + "正在跳舞");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "BigStar{name = " + name + "}";
}
}
//代理類
// 創(chuàng)建代理 java.lang.reflect.Proxy類:提供了為對象產(chǎn)生代理對象的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
參數(shù)一:用于指定用哪個類加載器,去加載生成的代理類
參數(shù)二:指定接口,這些接口用于指定生成的代理有哪些方法
參數(shù)三:用來指定生成的代理對象要干什么事情
public class ProxyUtil {
/**
* 形參:被代理的明星對象
* 返回值:給明星創(chuàng)建的代理
*/
public static Star createProxy(BigStar bigStar){
// 創(chuàng)建代理
// 這個代碼沒有顯示實現(xiàn)Star接口,但通過 JDK動態(tài)代理機制 隱式地為生成的代理類添加了Star接口的實現(xiàn),從而可以看作實現(xiàn)了Star接口
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//參數(shù)一:用于指定用哪個類加載器,去加載生成的代理類
new Class[]{Star.class},//參數(shù)二:指定接口,這些接口用于指定生成的代理有哪些方法
new InvocationHandler() { //參數(shù)三:用來指定生成的代理對象要干什么事情
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 參數(shù)一:代理的對象,即star變量引用的對象
* 參數(shù)二:要運行的方法,sing,dance
* 參數(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 class Test {
public static void main(String[] args) {
//1. 獲取代理的對象
BigStar bigStar = new BigStar("機哥");
Star proxy = ProxyUtil.createProxy(bigStar);
//2. 調(diào)用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}
2.4 動態(tài)代理擴展
動態(tài)代理,還可以攔截方法。比如:在這個故事中,經(jīng)紀(jì)人作為代理,如果別人讓邀請大明星去唱歌,打籃球,經(jīng)紀(jì)人就增強功能。但是如果別人讓大明星去掃廁所,經(jīng)紀(jì)人就要攔截,不會去調(diào)用大明星的方法。
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;
}
}總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決mybatis一對多查詢resultMap只返回了一條記錄問題
小編接到領(lǐng)導(dǎo)一個任務(wù)需求,需要用到使用resultMap相關(guān)知識,在這小編記錄下這個問題的解決方法,對mybatis一對多查詢resultMap項目知識感興趣的朋友一起看看吧2021-11-11
淺析JDBC與數(shù)據(jù)庫如何進行數(shù)據(jù)交互及類型轉(zhuǎn)換
本文主要介紹了JDBC在Java應(yīng)用程序與數(shù)據(jù)庫交互中的關(guān)鍵作用,詳細解釋了數(shù)據(jù)交互流程和數(shù)據(jù)類型轉(zhuǎn)換機制,并提供了高級數(shù)據(jù)交互機制、優(yōu)化建議及實際驅(qū)動示例,希望對大家有所幫助2026-04-04
Spring中ResponseBodyAdvice的使用詳解
這篇文章主要介紹了Spring中ResponseBodyAdvice的使用,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10
2022?最新?IntelliJ?IDEA?詳細配置步驟演示(推薦)
作為一名開發(fā)人員,第一肯定是選擇一款趁手的開發(fā)利器,本人使用?Java?偏多,這里推薦使用?IntelliJ?IDEA,?俗稱神級開發(fā)工具,具體的安裝過程就不過多贅述了,有需要了解的朋友可以參考下本文2022-09-09
深入解析面向?qū)ο缶幊淘贘ava中的應(yīng)用小結(jié)
本文詳細介紹了面向?qū)ο缶幊痰幕靖拍?包括類和對象、封裝、繼承和多態(tài),通過具體的Java代碼示例,展示了如何在Java中應(yīng)用這些面向?qū)ο缶幊痰暮诵乃枷?感興趣的朋友跟隨小編一起看看吧2025-01-01
學(xué)習(xí)Java設(shè)計模式之觀察者模式
這篇文章主要為大家介紹了Java設(shè)計模式中的觀察者模式,對Java設(shè)計模式感興趣的小伙伴們可以參考一下2016-01-01
Java線程中的用戶態(tài)和內(nèi)核態(tài)解讀
這篇文章主要介紹了Java線程中的用戶態(tài)和內(nèi)核態(tài)解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06

