自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 反射:讓你更優(yōu)雅的使用框架!

開發(fā)
毫不夸張地說,沒有反射,很多優(yōu)秀的框架不復(fù)存在,沒有這些優(yōu)秀的框架(比如Spring),Java可能會(huì)遜色很多,因此,這篇文章,我們一起來深入探討Java反射以及其背后的原理。

在 Java語言中,反射是一種強(qiáng)大而優(yōu)秀的機(jī)制,通過反射,我們可以在運(yùn)行時(shí)檢查和修改類、接口、字段和方法的信息,甚至動(dòng)態(tài)地創(chuàng)建對(duì)象、調(diào)用方法和訪問私有成員。

可以毫不夸張地說,沒有反射,很多優(yōu)秀的框架不復(fù)存在,沒有這些優(yōu)秀的框架(比如Spring),Java可能會(huì)遜色很多,因此,這篇文章,我們一起來深入探討Java反射以及其背后的原理。

一、什么是反射

先看看 Oracle官方對(duì)java反射的說明:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions. The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

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

它是通過 Java反射 API 來實(shí)現(xiàn),其中最核心的類位于 java.lang.reflect 包下,如 Class、Constructor、Field 和 Method等,這些類提供了對(duì)類和對(duì)象的運(yùn)行時(shí)信息進(jìn)行檢查和操作的方法。如下圖,展示了 JDK源碼中 java.lang.reflect 包所有的類:

二、反射的原理

反射的原理主要可以從下面 4個(gè)點(diǎn)來闡述:

  • 類加載:當(dāng) Java程序運(yùn)行時(shí),類加載器會(huì)根據(jù)類的名稱查找并加載類的字節(jié)碼文件,然后將字節(jié)碼文件轉(zhuǎn)換為可執(zhí)行的 Java類,并將其存儲(chǔ)在運(yùn)行時(shí)數(shù)據(jù)區(qū)域的方法區(qū)中。
  • 創(chuàng)建 Class對(duì)象:在類加載過程中,Java虛擬機(jī)會(huì)自動(dòng)創(chuàng)建對(duì)應(yīng)的Class對(duì)象,Class對(duì)象包含了類的元數(shù)據(jù)信息,并提供了訪問和操作類的接口。
  • 獲取 Class對(duì)象:Class對(duì)象通過多種方式獲取,最常見的方式有 3種: 類的 .class屬性、類實(shí)例的 getClass()方法、Class.forName()。
  • 訪問和操作:通過Class對(duì)象獲取類的字段、方法、構(gòu)造函數(shù)等信息,使用Field類和Method類來訪問和操作字段和方法,甚至可以調(diào)用私有的字段和方法。

通過上述的分析可以看出:反射機(jī)制需要基于Java虛擬機(jī)對(duì)類的加載、存儲(chǔ)和訪問機(jī)制的支持,通過反射,可以在運(yùn)行時(shí)動(dòng)態(tài)地探索和操作類的信息,實(shí)現(xiàn)靈活的編程和代碼的動(dòng)態(tài)行為。

三、如何使用反射

在講解了 Java反射原理之后,我們通過一個(gè)真實(shí)的例子來展示如何使用 Java反射機(jī)制。如下示例 demo,通過反射給 Person 類中的 greet() 方法傳入一個(gè) name,然后輸出:

過程分析:

  • 首先,在示例代碼通過獲Person.class取了 Person的Class對(duì)象;
  • 然后,使用clazz.getName()獲取了類的名稱,通過clazz.getModifiers()獲取了類的修飾符,并打印輸出;
  • 接下來,通過clazz.getDeclaredMethods()獲取類的所有方法,并依次打印輸出方法的名稱;
  • 接著,通過clazz.getDeclaredConstructor().newInstance()方法創(chuàng)建了 Person 的實(shí)例;
  • 再接著,使用clazz.getDeclaredMethod()方法獲取了 greet()方法的引用。為了調(diào)用私有方法,我們需要調(diào)用setAccessible(true)來設(shè)置方法的可訪問性。
  • 最后,使用Method.invoke()方法調(diào)用了 greet()方法,傳遞參數(shù)name = Java。

運(yùn)行示例結(jié)果如下圖:

上述示例,我們通過詳細(xì)的步驟展示了如何使用反射獲取類的信息和動(dòng)態(tài)調(diào)用方法。你也可以嘗試在 Person 中添加更多的方法和字段,并使用反射來獲取和操作它們。

四、部分源碼解讀

在上述示例講解時(shí),最后是調(diào)用 Method.invoke() 實(shí)現(xiàn) Person.greet()的調(diào)用,因此,這里我們主要分析 invoke()方案,官方源碼截圖:

從上面源碼截圖看出:Method.invoke() 方法,真實(shí)返回的是接口 MethodAccessor.invoke()方法。MethodAccessor 接口有三個(gè)實(shí)現(xiàn)類,具體是調(diào)用哪個(gè)類的 invoke 方法?

進(jìn)入acquireMethodAccessor方法,可以看到MethodAccessor由ReflectionFactory 的 newMethodAccessor方法決定。

再進(jìn)入 DelegatingMethodAccessorImpl 的 invoke方法:

DelegatingMethodAccessorImpl的invoke方法返回的是MethodAccessorImpl的invoke方法,而MethodAccessorImpl的invoke方法,由它的子類NativeMethodAccessorImpl重寫,這時(shí)候返回的是native invoke0,如下圖:

跟到源碼最后可以發(fā)現(xiàn):Method.invoke()方法最終調(diào)用 native的invoke0(),應(yīng)用層面的操作最終轉(zhuǎn)換成對(duì)操作系統(tǒng) c/c++方法的調(diào)用。

五、反射優(yōu)缺點(diǎn)

上面內(nèi)容的講解已經(jīng)側(cè)面反映出了Java反射的一些優(yōu)點(diǎn),這里再詳細(xì)的總結(jié)下反射的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  • 動(dòng)態(tài)性:反射允許我們?cè)谶\(yùn)行時(shí)動(dòng)態(tài)地獲取和操作類的信息,而不需要在編譯時(shí)確定。這為編寫靈活的、可擴(kuò)展的代碼提供了便利。
  • 靈活性:通過反射,我們可以繞過訪問修飾符的限制,訪問和修改私有成員、調(diào)用私有方法等。這為我們?cè)谔厥馇闆r下進(jìn)行一些高級(jí)操作提供了可能。
  • 框架開發(fā):反射在開發(fā)框架和庫時(shí)非常有用。通過反射,框架可以動(dòng)態(tài)地加載和實(shí)例化類,解析注解,處理回調(diào)等。這為框架提供了更大的靈活性和可擴(kuò)展性。
  • 調(diào)試和探索:反射使得我們可以在運(yùn)行時(shí)探索代碼背后的信息,例如獲取類的結(jié)構(gòu)、方法、字段等。這對(duì)于調(diào)試和理解復(fù)雜的代碼非常有幫助。

缺點(diǎn):

  • 性能開銷:相比于直接調(diào)用代碼,使用反射會(huì)帶來更高的性能開銷。反射涉及到動(dòng)態(tài)查找、方法調(diào)用等操作,這些操作比直接調(diào)用代碼更加耗時(shí)。因此,在對(duì)性能要求較高的場(chǎng)景下,過度使用反射可能導(dǎo)致性能下降。
  • 安全性和穩(wěn)定性:反射打破了封裝性和類型安全性,通過反射,我們可以繞過訪問修飾符的限制,調(diào)用私有方法等,這可能導(dǎo)致代碼的不穩(wěn)定性和安全隱患。因此,使用反射時(shí)需要格外小心,確保代碼的正確性和穩(wěn)定性。

從整體上看,Java反射是以犧牲了小部分的性能換取了更好的擴(kuò)展性和靈活性,犧牲小我成就大我,而且,隨著現(xiàn)代硬件設(shè)備能力越來越強(qiáng),這點(diǎn)小性能的犧牲是完全值得的。

六、為什么需要反射 

反射機(jī)制在 Java中的作用不言而喻,下面列舉了反射機(jī)制的一些常見場(chǎng)景和原因:

  • 運(yùn)行時(shí)類型檢查:反射機(jī)制允許在運(yùn)行時(shí)獲取類的信息,包括字段、方法和構(gòu)造方法等。因此,在進(jìn)行運(yùn)行時(shí)類型檢查,以確保代碼在處理不同類型的對(duì)象時(shí)能夠正確地進(jìn)行操作。
  • 動(dòng)態(tài)創(chuàng)建對(duì)象:通過反射,可以在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建對(duì)象,而不需要在編譯時(shí)知道具體的類名。這對(duì)于某些需要根據(jù)條件或配置來創(chuàng)建對(duì)象的情況非常有用,例如工廠模式或依賴注入框架。
  • 訪問和修改私有成員:反射機(jī)制可以繞過訪問權(quán)限限制,訪問和修改類的私有字段和方法。雖然這破壞了封裝性原則,但在某些特定情況下,這種能力可以幫助我們進(jìn)行一些特殊操作,例如單元測(cè)試、調(diào)試或框架的內(nèi)部實(shí)現(xiàn)。
  • 動(dòng)態(tài)調(diào)用方法:反射機(jī)制允許我們?cè)谶\(yùn)行時(shí)動(dòng)態(tài)地調(diào)用類的方法,甚至可以根據(jù)運(yùn)行時(shí)的條件來選擇不同的方法。這對(duì)于實(shí)現(xiàn)插件化系統(tǒng)、處理回調(diào)函數(shù)或?qū)崿F(xiàn)動(dòng)態(tài)代理等功能非常有用。
  • 框架和庫的實(shí)現(xiàn):許多Java框架和庫在其實(shí)現(xiàn)中廣泛使用了反射機(jī)制。它們利用反射來自動(dòng)發(fā)現(xiàn)和加載類、實(shí)現(xiàn)依賴注入、處理注解、配置文件解析和動(dòng)態(tài)代理等。反射機(jī)制使得這些框架和庫更加靈活和擴(kuò)展。

七、常用框架 

很多優(yōu)秀的框架內(nèi)部都使用了Java反射,這里重點(diǎn)講解下給 Java打下半壁江山的 Spring生態(tài)(Spring Framework,Spring MVC,SpringBoot, SpringCloud...),以 Spring Framework為例:

  • 依賴注入(Dependency Injection) : 依賴注入,可以把程序員主動(dòng)創(chuàng)建對(duì)象的事情交給 Spring管理,大大提升了對(duì)象創(chuàng)建的靈活性。當(dāng)我們?cè)谂渲梦募蛴米⒔舛x Bean時(shí),Spring會(huì)使用反射來動(dòng)態(tài)地實(shí)例化對(duì)象,并將依賴的其他對(duì)象注入到這些實(shí)例中。
  • 自動(dòng)裝配(Autowired) : 當(dāng) Spring容器啟動(dòng)時(shí),它會(huì)掃描應(yīng)用程序中的所有類,并使用反射來查找和識(shí)別帶有 @Autowired注解的字段、方法或構(gòu)造函數(shù)。再自動(dòng)將 Bean注入到需要的位置,實(shí)現(xiàn)對(duì)象之間的自動(dòng)連接。
  • AOP(Aspect-Oriented Programming) : AOP 利用了動(dòng)態(tài)代理和反射機(jī)制。通過定義切面(Aspect)和切點(diǎn)(Pointcut),Spring可以在運(yùn)行時(shí)使用反射來創(chuàng)建代理對(duì)象,從而實(shí)現(xiàn)橫切關(guān)注點(diǎn)(cross-cutting concerns)的功能,如日志記錄、事務(wù)管理等。
  • 動(dòng)態(tài)代理(Dynamic Proxy) : Spring利用 Java反射機(jī)制動(dòng)態(tài)地創(chuàng)建代理對(duì)象,并在代理對(duì)象中添加額外的邏輯,從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的增強(qiáng)。
  • 框架擴(kuò)展和定制: Spring通過反射機(jī)制來實(shí)現(xiàn)對(duì)應(yīng)用程序的擴(kuò)展和定制的。例如,Spring提供了BeanPostProcessor接口,允許開發(fā)人員在 Bean初始化前后插入自定義邏輯,這是通過反射來實(shí)現(xiàn)的。

另外,還有一些耳熟能詳?shù)目蚣芤彩褂昧薐ava反射:

  • JUnit:JUnit是一個(gè)優(yōu)秀的單元測(cè)試框架,它利用了 Java反射機(jī)制動(dòng)態(tài)地加載和執(zhí)行測(cè)試方法。
  • Jackson:Jackson是一個(gè) JSON處理的 Java庫,它利用反射來實(shí)現(xiàn) JSON與 Java對(duì)象之間的轉(zhuǎn)換,動(dòng)態(tài)讀取和寫入 Java對(duì)象的屬性,并將其轉(zhuǎn)換為 JSON格式。
  • Hibernate ORM:Hibernate和 MyBatis一樣,都是對(duì)象關(guān)系映射框架,通過反射來實(shí)現(xiàn)對(duì)象與數(shù)據(jù)庫表之間的映射關(guān)系。

八、總結(jié) 

本文講解了Java反射的原理和使用方式,因?yàn)橛辛薐ava反射,很多優(yōu)秀的框架應(yīng)運(yùn)而生,從而使得 Java 生態(tài)越來越完善,因此,反射是絕大多數(shù)框架的基石。

Java反射有優(yōu)點(diǎn)也有缺點(diǎn),從整體上看,Java反射是以犧牲了小部分的性能換取了更好的擴(kuò)展性和靈活性,犧牲小我成就大我,而且,隨著現(xiàn)代硬件設(shè)備能力越來越強(qiáng),這點(diǎn)小性能的犧牲是完全值得的。

掌握J(rèn)ava反射,我們可以更好的理解一些優(yōu)秀框架的運(yùn)行機(jī)制,比如:Spring。它可以幫助我們更好的使用框架,遇到問題時(shí)也能更好的去分析和解決。

責(zé)任編輯:趙寧寧 來源: 猿java
相關(guān)推薦

2022-03-08 06:41:35

css代碼

2020-04-03 14:55:39

Python 代碼編程

2019-09-29 16:17:25

Java代碼性能編程語言

2022-04-10 10:41:17

ESLint異步代碼

2023-11-23 13:50:00

Python代碼

2022-11-18 08:32:23

spring參數(shù)解析器

2018-07-12 14:20:33

SQLSQL查詢編寫

2022-12-26 07:47:37

JDK8函數(shù)式接口

2023-07-10 09:39:02

lambdaPython語言

2025-02-10 00:25:00

命令模式擴(kuò)展機(jī)制系統(tǒng)

2025-04-21 17:55:25

2017-09-27 16:09:29

代碼

2024-12-31 08:54:38

2023-12-21 10:26:30

??Prettier

2022-05-13 08:48:50

React組件TypeScrip

2024-07-03 08:13:56

規(guī)則執(zhí)行器代碼

2024-08-20 14:25:20

2024-07-30 14:09:19

裝飾器Python代碼

2021-06-25 15:53:25

Kubernetes程序技巧

2022-03-11 12:14:43

CSS代碼前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)