Java反射Reflect機制詳解

文章目錄

  • 引言
  • 反射的基本概念
  • 反射基本原理
  • 反射應用場景
  • 反射基本使用
    • 獲取類的Class對象
    • 獲取構造方法并實例化對象
    • 獲取和調用方法
    • 獲取和修改字段
    • 反射工具類
  • 反射源碼解讀
    • 獲取Class對象的源碼
    • 調用方法的源碼
  • 反射優缺點
    • 優點
    • 缺點
  • 為什么需要反射
  • 總結

引言

Java反射是Java語言中的一種動態機制,它允許在運行時檢查和操作類的結構和行為。反射的強大功能使得程序可以在運行時動態加載類、調用方法和訪問字段,從而極大地增強了Java程序的靈活性和擴展性。

反射的基本概念

反射(Reflection)是指程序在運行時能夠自我檢查和操作自身的能力。通過反射,可以獲取類的構造器、方法、字段等信息,并能動態調用對象的方法、設置或獲取對象的字段值。

反射關鍵信息

  • Class: 代表類的實體,在運行時加載類時會創建對應的Class對象。
  • Constructor: 代表類的構造方法。
  • Method: 代表類的方法。
  • Field: 代表類的字段。

Java反射最核心的類位于JDK源碼 java.lang.reflect包下,比如Class、Constructor、Field 和 Method等,他們提供了對類和對象運行時信息進行檢查和操作的方法。

Java反射Reflect機制詳解 - Java技術債務

反射基本原理

Java反射的核心在于Class類,它包含了關于類的所有信息。在Java虛擬機(JVM)加載類時,會為每個類創建一個對應的Class對象,該對象保存了類的元數據。通過這些元數據,程序可以在運行時獲取類的詳細信息并進行操作。

主要可以從下面 4個點來闡述:

  1. 類加載:當 Java程序運行時,類加載器會根據類的名稱查找并加載類的字節碼文件,然后將字節碼文件轉換為可執行的 Java類,并將其存儲在運行時數據區域的方法區中。
  2. 創建 Class對象:在類加載過程中,Java虛擬機會自動創建對應的Class對象,Class對象包含了類的元數據信息,并提供了訪問和操作類的接口。
  3. 獲取 Class對象:Class對象通過多種方式獲取,最常見的方式有 3種: 類的 .class屬性、類實例的 getClass()方法、Class.forName()。
  4. 訪問和操作:通過Class對象獲取類的字段、方法、構造函數等信息,使用Field類和Method類來訪問和操作字段和方法,甚至可以調用私有的字段和方法。

通過上述的分析可以看出:反射機制需要基于Java虛擬機對類的加載、存儲和訪問機制的支持,通過反射,可以在運行時動態地探索和操作類的信息,實現靈活的編程和代碼的動態行為。

反射應用場景

很多優秀的框架內部都使用了Java反射,這里重點講解下給 Java打下半壁江山的 Spring生態(Spring Framework,Spring MVC,SpringBoot, SpringCloud…),以 Spring Framework為例:

  1. 依賴注入(Dependency Injection) : 依賴注入,可以把程序員主動創建對象的事情交給 Spring管理,大大提升了對象創建的靈活性。當我們在配置文件或用注解定義 Bean時,Spring會使用反射來動態地實例化對象,并將依賴的其他對象注入到這些實例中。
  2. 自動裝配(Autowired) : 當 Spring容器啟動時,它會掃描應用程序中的所有類,并使用反射來查找和識別帶有 @Autowired注解的字段、方法或構造函數。再自動將 Bean注入到需要的位置,實現對象之間的自動連接。
  3. AOP(Aspect-Oriented Programming) : AOP 利用了動態代理和反射機制。通過定義切面(Aspect)和切點(Pointcut),Spring可以在運行時使用反射來創建代理對象,從而實現橫切關注點(cross-cutting concerns)的功能,如日志記錄、事務管理等。
  4. 動態代理(Dynamic Proxy) : Spring利用 Java反射機制動態地創建代理對象,并在代理對象中添加額外的邏輯,從而實現對目標對象的增強。
  5. 框架擴展和定制: Spring通過反射機制來實現對應用程序的擴展和定制的。例如,Spring提供了BeanPostProcessor接口,允許開發人員在 Bean初始化前后插入自定義邏輯,這是通過反射來實現的。

另外,還有一些耳熟能詳的框架也使用了Java反射

  1. JUnit:JUnit是一個優秀的單元測試框架,它利用了 Java反射機制動態地加載和執行測試方法。
  2. Jackson:Jackson是一個 JSON處理的 Java庫,它利用反射來實現 JSON與 Java對象之間的轉換,動態讀取和寫入 Java對象的屬性,并將其轉換為 JSON格式。
  3. Hibernate ORM:Hibernate和 MyBatis一樣,都是對象關系映射框架,通過反射來實現對象與數據庫表之間的映射關系。

總結以下幾點:

  • 框架設計: 許多Java框架(如Spring、Hibernate)廣泛使用反射來實現依賴注入、面向切面編程等功能。
  • 調試和測試: 反射允許動態訪問和修改對象,方便調試和測試私有方法和字段。
  • 動態代理: 通過反射實現動態代理,增強程序的靈活性和可擴展性。
  • 類瀏覽器和可視化工具: 反射幫助開發工具展示類的結構和關系。

反射基本使用

獲取類的Class對象

Class<?> clazz = Class.forName("com.example.MyClass");
// 或者
Class<?> clazz = MyClass.class;
// 或者
Class<?> clazz = myObject.getClass();

獲取構造方法并實例化對象

Constructor<?> constructor = clazz.getConstructor(String.class);
Object instance = constructor.newInstance("example");

獲取和調用方法

Method method = clazz.getMethod("myMethod", String.class);
method.invoke(instance, "Hello");

獲取和修改字段

Field field = clazz.getDeclaredField("myField");
// 允許訪問私有字段
field.setAccessible(true); 
field.set(instance, "New Value");
String value = (String) field.get(instance);

反射工具類

@Slf4j
public class ReflectionUtil {/*** 獲取屬性名以及對應的屬性值** @param o 對象* @return map*/public static Map<String, Object> getFieldNameAndValue(Object o) {Map<String, Object> resMap = new LinkedHashMap<>();Class<?> clazz = o.getClass();while (Objects.nonNull(clazz)) {Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {String fieldName = field.getName();if (Objects.isNull(resMap.get(fieldName))) {resMap.put(fieldName, getFieldValueByName(fieldName, o));}}clazz = clazz.getSuperclass();}return resMap;}/*** 獲取屬性名以及對應的屬性值** @param list 對象數組* @return list\<map\>*/public static List<Map<String, Object>> getFieldNameAndValueMaps(List<?> list) {List<Map<String, Object>> resMaps = new ArrayList<>(list.size());for (Object o : list) {if (Objects.isNull(o)) {throw new RuntimeException("Arrays Cannot Contain Null... ...");}Map<String, Object> fieldNameAndValueMap = getFieldNameAndValue(o);resMaps.add(fieldNameAndValueMap);}return resMaps;}public static List<String> getFieldNames(Class<?> clazz) {List<String> fieldList = new ArrayList<>();while (Objects.nonNull(clazz)) {Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {String fieldName = field.getName();fieldList.add(fieldName);}clazz = clazz.getSuperclass();}return fieldList;}/*** 獲取類上的指定字段。如果在類本身上找不到該字段,則將遞歸檢查超類。** @param clazz     source class* @param fieldName 字段名* @return 字段*/private static Field getField(Class<?> clazz, String fieldName) {try {return clazz.getDeclaredField(fieldName);} catch (NoSuchFieldException nsf) {if (clazz.getSuperclass() != null) {return getField(clazz.getSuperclass(), fieldName);}throw new IllegalStateException("Could not locate field '" + fieldName + "' on class " + clazz);}}/*** 根據屬性名獲取屬性值** @param fieldName 屬性名稱* @param o         對象* @return Object*/public static Object getFieldValueByName(String fieldName, Object o) {try {String firstLetter = fieldName.substring(0, 1).toUpperCase();String getter = "get" + firstLetter + fieldName.substring(1);Method method = o.getClass().getMethod(getter);return method.invoke(o);} catch (Exception e) {log.info("根據屬性名獲取屬性值異常:" + e.getMessage() + "\n" + e);return null;}}@SuppressWarnings({"all"})public static void setFiledValue(Object bean, String filedName, Object value) throws NoSuchFieldException, IllegalAccessException {Class<?> aClass = bean.getClass();Field field = getField(aClass, filedName);field.setAccessible(true);field.set(bean, value);}
}

反射源碼解讀

反射的實現依賴于JVM提供的本地方法接口(JNI),通過調用本地方法實現對類信息的獲取和操作。

獲取Class對象的源碼

public static Class<?> forName(String className) throws ClassNotFoundException {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();if (classLoader == null) {classLoader = ClassLoader.getSystemClassLoader();}return classLoader.loadClass(className);
}

調用方法的源碼

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class<?> caller = Reflection.getCallerClass();checkAccess(caller, clazz, obj, modifiers);}}MethodAccessor ma = methodAccessor;             // read volatileif (ma == null) {ma = acquireMethodAccessor();}return ma.invoke(obj, args);
}

從源碼可以看出:Method.invoke()方法,真實返回的是接口MethodAccessor.invoke()方法。MethodAccessor接口有三個實現類,具體是調用哪個類的 invoke 方法?

Java反射Reflect機制詳解 - Java技術債務

跟到源碼最后可以發現:Method.invoke()方法最終調用 native的invoke0(),應用層面的操作最終轉換成對操作系統 c/c++方法的調用。

反射優缺點

優點

  • 靈活性: 反射允許在運行時動態操作類,提高了程序的靈活性和擴展性。
  • 動態代理: 通過反射可以實現動態代理機制,廣泛應用于AOP(面向切面編程)等領域。
  • 通用性: 反射可以用來編寫通用的框架和庫,增強代碼的重用性。

缺點

  • 性能開銷: 反射操作較為耗時,可能會影響程序性能。
  • 安全問題: 反射可以繞過訪問控制,修改私有字段和方法,可能引發安全問題。
  • 代碼復雜性: 使用反射可能增加代碼的復雜性和維護難度。

為什么需要反射

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

  1. 運行時類型檢查:反射機制允許在運行時獲取類的信息,包括字段、方法和構造方法等。因此,在進行運行時類型檢查,以確保代碼在處理不同類型的對象時能夠正確地進行操作。
  2. 動態創建對象:通過反射,可以在運行時動態地創建對象,而不需要在編譯時知道具體的類名。這對于某些需要根據條件或配置來創建對象的情況非常有用,例如工廠模式或依賴注入框架。
  3. 訪問和修改私有成員:反射機制可以繞過訪問權限限制,訪問和修改類的私有字段和方法。雖然這破壞了封裝性原則,但在某些特定情況下,這種能力可以幫助我們進行一些特殊操作,例如單元測試、調試或框架的內部實現。
  4. 動態調用方法:反射機制允許我們在運行時動態地調用類的方法,甚至可以根據運行時的條件來選擇不同的方法。這對于實現插件化系統、處理回調函數或實現動態代理等功能非常有用。
  5. 框架和庫的實現:許多Java框架和庫在其實現中廣泛使用了反射機制。它們利用反射來自動發現和加載類、實現依賴注入、處理注解、配置文件解析和動態代理等。反射機制使得這些框架和庫更加靈活和擴展。

總結

Java反射是一個強大的工具,極大地增強了Java語言的動態性和靈活性。然而,在使用反射時需要權衡其性能開銷和安全風險。Java反射有優點也有缺點,從整體上看,Java反射是以犧牲了小部分的性能換取了更好的擴展性和靈活性,犧牲小我成就大我,而且,隨著現代硬件設備能力越來越強,這點小性能的犧牲是完全值得的。理解反射的原理和使用場景,可以更好地應用反射技術來解決實際開發中的問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/23636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/23636.shtml
英文地址,請注明出處:http://en.pswp.cn/web/23636.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【干貨】視頻文件抽幀(opencv和ffmpeg方式對比)

1 廢話不多說&#xff0c;直接上代碼 opencv方式 import time import subprocess import cv2, os from math import ceildef extract_frames_opencv(video_path, output_folder, frame_rate1):"""使用 OpenCV 從視頻中抽取每秒指定幀數的幀,并保存到指定文件夾…

linux系統使用達夢數據庫

在Linux系統中使用達夢數據庫&#xff0c;首先需要確保已經正確安裝了達夢數據庫軟件。以下是一個基本的使用示例&#xff0c;假設您已經安裝了達夢數據庫并且配置好了相關環境變量。 連接到數據庫&#xff1a; 使用 dsql 命令連接到數據庫 dsql -h hostname -u username -p…

寶貝,帶上WebAssembly,換個姿勢來優化你的前端應用

在你沒崛起之前,臉是用來丟的 大家好,我是柒八九。一個專注于前端開發技術/Rust及AI應用知識分享的Coder 此篇文章所涉及到的技術有 WebAssemblyRustWeb Worker(comlink)wasm-packPhotonffmpeg.wasm腳手架生成前端項目因為,行文字數所限,有些概念可能會一帶而過亦或者提供對…

BOM是什么東西

BOM&#xff08;Byte Order Mark&#xff0c;字節順序標記&#xff09;是一個Unicode字符&#xff0c;通常出現在文本文件的開頭。它的作用包括以下幾個方面&#xff1a; 1. 指示文件的編碼方式 BOM可以幫助軟件識別文本文件使用的字符編碼。不同的編碼方式可能會使用不同的B…

經濟與安全兼顧:茶飲店購買可燃氣體報警器的價格考量

可燃氣體報警器在如今的社會中扮演著至關重要的角色。它們用于檢測環境中的可燃氣體濃度&#xff0c;及早發現潛在的火災隱患&#xff0c;保護人們的生命和財產安全。 在這篇文章中&#xff0c;佰德將介紹可燃氣體報警器的安裝、檢定以及價格&#xff0c;通過實際案例和數據&a…

PCL 生成空間橢圓點云

目錄 一、算法原理二、代碼實現三、結果展示本文由CSDN點云俠原創,原文鏈接。如果你不是在點云俠的博客中看到該文章,那么此處便是不要臉的爬蟲。 一、算法原理 設橢圓在 X O Y XOY XOY平面上,參數方程為:

怎么保障TikTok直播網絡穩定?

TikTok&#xff0c;這個近年來風靡全球的社交媒體平臺&#xff0c;已成為電商引流的新方向&#xff0c;尤其是其直播功能。然而&#xff0c;對于打算進軍TikTok直播領域的商家和主播而言&#xff0c;確保網絡穩定無疑是首要任務。那么&#xff0c;TikTok直播專線究竟是什么呢&a…

牛啊后續:如何一行C#代碼實現解析類型的Summary注釋(可用于數據字典快速生成)...

前言&#xff1a;下午有小伙伴要求&#xff0c;讓我繼續做個解析實體類注釋信息的內容。所以我也順便加入進來。以下開始正文實戰操作&#xff1a; 項目需要勾選輸出api文檔文件。這樣就可以讓所有實體類的summary信息被寫入到輸出目錄下。如果有多個xml文件也沒關系&#xff0…

小程序 UI 風格美不勝收

小程序 UI 風格美不勝收 小程序 UI 風格美不勝收

PostgreSQL的視圖pg_stat_replication

PostgreSQL的視圖pg_stat_replication pg_stat_replication 是 PostgreSQL 提供的一個系統視圖&#xff0c;用于顯示主服務器上當前正在進行的復制會話的信息。它可以幫助數據庫管理員監控和管理主從復制的狀態&#xff0c;確保數據的正確同步和高可靠性。 pg_stat_replicati…

MyEclipse中properties文件中文亂碼(Unicode字符)解決辦法

程序代碼園發文地址&#xff1a;MyEclipse中properties文件中文亂碼&#xff08;Unicode字符&#xff09;解決辦法-程序代碼園小說,Java,HTML,Java小工具,程序代碼園,http://www.byqws.com/ ,MyEclipse中properties文件中文亂碼&#xff08;Unicode字符&#xff09;解決辦法htt…

Django學習三:views業務層中通過models對實體對象進行的增、刪、改、查操作。

文章目錄 前言一、Django ORM介紹二、項目快速搭建三、操作1、view.pya、增加操作b、刪除操作c、修改操作d、查詢操作 2、urls.py 前言 上接博文&#xff1a;Django學習二&#xff1a;配置mysql&#xff0c;創建model實例&#xff0c;自動創建數據庫表&#xff0c;對mysql數據…

一周發文9篇!MIMIC-IV數據庫周報(5.22~5.28)

重癥醫學數據庫&#xff08;MIMIC&#xff09;是由計算生理學實驗室開發的公開數據集&#xff0c;其中包括與數千個重癥監護病房入院相關的去識別化健康數據&#xff0c;致力于推動臨床信息學、流行病學和機器學習的研究。 MIMIC數據庫于2003年在美國國立衛生研究院的資助下&am…

2024上海初中生古詩文大會倒計時4個半月:單選題真題示例和獨家解析

現在距離2024年初中生古詩文大會還有4個半月時間&#xff0c;我們來看10道選擇題真題和詳細解析&#xff0c;了解古詩文大會的考察方式和知識點&#xff0c;從而更好地備考。 以下題目截取自我獨家制作的在線真題集&#xff0c;都是來自于歷屆真題&#xff0c;去重、合并后&am…

數據倉庫緩慢變化維介紹

緩慢變化維&#xff08;Slowly Changing Dimensions, SCD&#xff09;是數據倉庫設計中的一個重要概念&#xff0c;用于處理維度表中隨時間緩慢變化的屬性。維度表中的數據通常描述業務實體&#xff08;如客戶、產品、員工等&#xff09;&#xff0c;而這些實體的某些屬性&…

面試成功的不二法門:詳解Vue3答題章法

前言 面試題在網絡上有如海洋之深&#xff0c;對于同一知識點&#xff0c;每個人的理解也各有千秋。我們在面試中常常會遇到一個瞬息間腦海里一片空白的情況&#xff0c;其實這并不是因為我們不懂&#xff0c;而是因為我們在回答的時候缺乏一個清晰的思路。那么問題來了&#x…

《魔法與科技的融合:SpringBoot運維的現代傳說》

揭開了SpringBoot應用部署的神秘面紗。從云平臺的選型到Docker的容器化魔法&#xff0c;再到Kubernetes的集群力量&#xff0c;每一步都充滿了奇幻色彩。文章以輕松幽默的筆觸&#xff0c;帶領讀者穿梭于現代應用部署的各個角落&#xff0c;探索自動化部署的奧秘&#xff0c;學…

關于圖像過曝問題的排查思路

1、問題背景 讀者提問&#xff0c;圖像在室外遇到過曝的問題&#xff0c;有什么排查思路和改善方法。 2、問題分析 1&#xff09;先檢查一下sensor驅動&#xff0c;對照 sensor datasheet 確認下最小曝光行設置的是否正確&#xff0c; 因為室外高亮場景一般曝光行走的都比較…

【深度學習】PuLID: Pure and Lightning ID Customization via Contrastive Alignment

論文&#xff1a;https://arxiv.org/abs/2404.16022 代碼&#xff1a;https://github.com/ToTheBeginning/PuLID 文章目錄 AbstractIntroductionRelated WorkMethods Abstract 我們提出了一種新穎的、無需調整的文本生成圖像ID定制方法——Pure and Lightning ID customizatio…

微信好友朋友圈的三天、半年可見怎么破?方法拿走不謝

『Code掘金』問大家&#xff0c;有沒有這種經歷&#xff0c;當你想去翻某人的朋友圈時&#xff0c;對方設置成了3天可見&#xff0c;之前的內容沒法看到了。 不過沒關系&#xff01;今天『Code掘金』給大家分享一款導出朋友圈的工具&#xff0c;讓大家留住痕跡。 WechatMomen…