Java-反射機制

在 Java 編程中,“反射” 是一個貫穿基礎與進階的核心概念,它允許程序在運行時動態獲取類的結構、調用方法、操作屬性,甚至創建對象 —— 無需在編譯期明確知道類的具體信息。

一、反射是什么?

首先明確一個關鍵定義:Java 反射(Reflection)是 Java 語言提供的一種能力,允許程序在運行時(而非編譯時)訪問、檢測和修改類、對象、方法、屬性等程序元素的信息

反射的核心價值

  • 動態性:打破編譯期的固定依賴,運行時靈活操作類結構(比如根據配置文件加載不同類)。
  • 通用性:可編寫通用框架(如 Spring、MyBatis),通過反射適配任意類,無需為每個類單獨寫代碼。
  • 穿透性:支持 “暴力反射”,可突破類成員的訪問權限限制(如操作 private 私有屬性 / 方法)。

二、反射的前提:獲取 “類對象”

反射的所有操作,都必須基于一個核心載體 ——Class 類的對象(簡稱 “類對象”)。每個類在 JVM 中只會被加載一次,因此同一個類的類對象全局唯一。

獲取類對象的 3 種核心方式

這 3 種方式對應類的 “生命周期”(硬盤→內存→對象)整理如下:

獲取方式語法示例適用場景關鍵說明
1. Class.forName (全類名)Class pClass = Class.forName("reflect.Person");編譯期未知類名,需動態指定(如讀配置文件)需傳入 “包名 + 類名”,會觸發類的加載,可能拋出?ClassNotFoundException
2. 類名.classClass pClass = Person.class;編譯期已知類名,需靜態獲取不會觸發類的加載,僅獲取類的靜態結構信息
3. 對象.getClass ()Person person = new Person(); Class pClass = person.getClass();已有對象實例,需通過對象反推類信息依賴具體對象,適用于 “已知對象但未知類” 的場景

驗證唯一性:同一個類的 3 種方式獲取的類對象地址完全相同

Class class1 = Class.forName("reflect.Person");
Class class2 = Person.class;
Person person = new Person();
Class class3 = person.getClass();// 輸出結果均為 true,證明類對象唯一
System.out.println(class1 == class2); 
System.out.println(class2 == class3);

三、反射核心操作:全方位操控類結構

獲取類對象后,即可通過反射 API 操作類的三大核心組件:成員變量、成員方法、構造方法。以下結合 Test.java 和 Person.java 的實踐代碼,分模塊梳理。

模塊 1:操作 “成員變量”(Field)

成員變量的反射操作,核心是 “獲取變量” 和 “讀寫變量值”,需區分 “所有權限” 和 “僅公共權限(public)”,同時支持暴力反射突破私有限制。

1.1 獲取成員變量的 4 個核心方法
方法名作用訪問權限范圍是否包含父類變量
getDeclaredFields()獲取當前類的所有成員變量任意權限(public/private/protected)不包含
getDeclaredField(String name)獲取當前類的指定名稱成員變量任意權限不包含
getFields()獲取當前類的所有公共成員變量僅 public包含父類的 public 變量
1.2 讀寫變量值:set () 與 get ()
  • 語法
    • 寫值:field.set(對象實例, 變量值)(為指定對象的該變量賦值)
    • 讀值:field.get(對象實例)(獲取指定對象的該變量值)
  • 關鍵注意:若變量是?private?私有權限,直接讀寫會拋出?IllegalAccessException,需先調用?field.setAccessible(true)?開啟 “暴力反射”,強制跳過權限檢查。
1.3 實戰代碼
// 1. 創建 Person 對象實例
Person person = new Person();
// 2. 獲取類對象
Class pClass = Class.forName("reflect.Person");// 3. 獲取所有成員變量(含 private)
Field[] allFields = pClass.getDeclaredFields();
for (Field field : allFields) {System.out.println(field); // 輸出:private java.lang.String reflect.Person.name、private int reflect.Person.age、public java.lang.String reflect.Person.from
}// 4. 獲取指定私有變量 name(需暴力反射)
Field nameField = pClass.getDeclaredField("name");
nameField.setAccessible(true); // 開啟暴力反射,突破 private 限制
nameField.set(person, "趙嘉成"); // 賦值
System.out.println(nameField.get(person)); // 取值,輸出:趙嘉成// 5. 獲取指定公共變量 from(無需暴力反射)
Field fromField = pClass.getDeclaredField("from");
fromField.set(person, "中國"); // 直接賦值
System.out.println(person); // 輸出:Person [name=趙嘉成, age=0, from=中國]

模塊 2:操作 “成員方法”(Method)

成員方法的反射操作,核心是 “獲取方法” 和 “執行方法”,同樣需區分權限范圍,支持暴力反射調用私有方法。

2.1 獲取成員方法的 4 個核心方法
方法名作用訪問權限范圍是否包含父類方法
getDeclaredMethods()獲取當前類的所有成員方法任意權限不包含
getDeclaredMethod(String name, Class<?>... paramTypes)獲取當前類的指定名稱和參數列表的方法任意權限不包含
getMethods()獲取當前類的所有公共成員方法僅 public包含父類的 public 方法(如 Object 的 toString ())
getMethod(String name, Class<?>... paramTypes)獲取當前類的指定名稱和參數列表的公共方法僅 public包含父類的 public 方法
2.2 執行方法:invoke ()

語法:method.invoke(對象實例, 方法參數值...)

  • 若方法是靜態方法(static),對象實例可傳?null
  • 若方法無參數,參數值部分可省略或傳空數組;
  • 若方法有返回值,invoke()?會返回該值(需強轉)。

權限注意:私有方法需先調用?method.setAccessible(true)?開啟暴力反射。

2.3 實戰代碼
// 1. 獲取類對象(省略,同上)
Class pClass = Class.forName("reflect.Person");
Person person = new Person();// 2. 獲取所有方法(含 private 的 run())
Method[] allMethods = pClass.getDeclaredMethods();
for (Method method : allMethods) {System.out.println(method); // 輸出:getAge()、setAge(int)、run() 等
}// 3. 獲取指定私有方法 run()(無參數)
Method runMethod = pClass.getDeclaredMethod("run");
runMethod.setAccessible(true); // 暴力反射突破 private
runMethod.invoke(person); // 執行 run() 方法(無返回值)// 4. 獲取指定公共方法 getAge()(無參數,有返回值)
Method getAgeMethod = pClass.getMethod("getAge");
int age = (int) getAgeMethod.invoke(person); // 執行并接收返回值
System.out.println(age); // 輸出:0(Person 初始 age 為 0)

模塊 3:操作 “構造方法”(Constructor)

構造方法的反射操作,核心是 “獲取構造器” 和 “創建對象”(替代?new?關鍵字),支持通過無參 / 有參構造器創建實例。

3.1 獲取構造方法的 4 個核心方法
方法名作用訪問權限范圍是否包含父類構造器
getDeclaredConstructors()獲取當前類的所有構造方法任意權限不包含(構造器不能繼承)
getDeclaredConstructor(Class<?>... paramTypes)獲取當前類的指定參數列表的構造方法任意權限不包含
getConstructors()獲取當前類的所有公共構造方法僅 public不包含
getConstructor(Class<?>... paramTypes)獲取當前類的指定參數列表的公共構造方法僅 public

不包含

3.2 創建對象:newInstance ()
  • 語法constructor.newInstance(構造參數值...)
    • 無參構造器:參數值部分可省略,直接?constructor.newInstance()
    • 有參構造器:需傳入與參數列表匹配的參數值;
    • 私有構造器:需先調用?constructor.setAccessible(true)?開啟暴力反射。
3.3 實戰代碼
// 1. 獲取類對象(省略)
Class pClass = Class.forName("reflect.Person");// 2. 獲取所有構造器(Person 有 3 個:無參、String name、String name+int age)
Constructor[] allConstructors = pClass.getDeclaredConstructors();
for (Constructor constructor : allConstructors) {System.out.println(constructor); // 輸出:Person()、Person(java.lang.String)、Person(java.lang.String,int)
}// 3. 獲取無參公共構造器,創建對象
Constructor noArgConstructor = pClass.getConstructor();
Person person1 = (Person) noArgConstructor.newInstance();
System.out.println(person1); // 輸出:Person [name=null, age=0, from=null]// 4. 獲取有參公共構造器(String name + int age),創建對象
Constructor twoArgConstructor = pClass.getConstructor(String.class, int.class);
Person person2 = (Person) twoArgConstructor.newInstance("麗麗", 20);
System.out.println(person2); // 輸出:Person [name=麗麗, age=20, from=null]

四、反射實戰:結合配置文件實現 “動態加載”

反射的核心優勢是 “動態性”,而結合配置文件(如 .properties)可實現 “不修改代碼,僅改配置就能切換類 / 方法”。Test.java 中已實現該場景,整理如下:

需求場景

通過?refconfig.properties?配置文件指定類名和方法名,運行時動態加載類并調用方法,無需硬編碼類名。

步驟拆解與代碼

創建配置文件(refconfig.properties):

# 配置要加載的類(全類名)
reflect.className=reflect.Person
# 配置要調用的方法名
reflect.methodName=getAge

讀取配置文件并動態反射:

// 1. 創建 Properties 對象,用于讀取配置文件
Properties props = new Properties();// 2. 通過類加載器加載配置文件(注意路徑:src/main/resources/reflect/refconfig.properties)
InputStream in = Person.class.getClassLoader().getResourceAsStream("reflect/refconfig.properties");
props.load(in); // 讀取配置內容// 3. 從配置中獲取類名和方法名
String className = props.getProperty("reflect.className");
String methodName = props.getProperty("reflect.methodName");// 4. 動態加載類,獲取類對象
Class dynamicClass = Class.forName(className);
System.out.println(dynamicClass); // 輸出:class reflect.Person// 5. 動態獲取方法并執行(這里以無參方法 getAge() 為例)
Method dynamicMethod = dynamicClass.getMethod(methodName);
Object instance = dynamicClass.getConstructor().newInstance(); // 創建對象
dynamicMethod.invoke(instance); // 執行方法,輸出:getAge方法執行

核心價值

若后續需要切換為操作?Cat.java(而非?Person.java),只需修改配置文件的?reflect.className=reflect.Cat,無需修改 Java 代碼 —— 這正是框架(如 Spring)“解耦” 的核心原理。

五、反射關鍵補充:類信息獲取與注意事項

5.1 獲取類的基本信息

通過類對象可快速獲取類的名稱、包名等信息,Test.java 中已實踐:

方法名作用示例(Person 類)
getName()獲取全類名(包名 + 類名)pClass.getName()?→ "reflect.Person"
getSimpleName()獲取簡單類名(僅類名)

pClass.getSimpleName()?→ "Person"

5.2 反射的注意事項

反射雖強大,但存在以下問題,使用時需謹慎:

  1. 性能開銷:反射操作需在運行時解析類結構,比直接調用(如?person.getAge())慢,高頻場景(如循環中)需避免。
  2. 安全風險:暴力反射突破了訪問權限限制,可能破壞類的封裝性(如修改私有變量),需確保操作的合理性。
  3. 代碼可讀性:反射代碼較抽象,不如直接調用直觀,需添加清晰注釋。
  4. 兼容性風險:若類結構修改(如方法名、參數列表變更),反射代碼可能拋出?NoSuchMethodException?等異常,需做好異常處理。

六、總結:反射知識體系圖譜

最后,用一張圖譜梳理本文核心內容,方便復習回顧:

Java 反射
├─ 核心前提:獲取類對象(3種方式)
│  ├─ Class.forName(全類名) → 動態加載
│  ├─ 類名.class → 靜態獲取
│  └─ 對象.getClass() → 實例反推
├─ 核心操作(3大組件)
│  ├─ 成員變量(Field):getDeclaredFields()/getFields() + set()/get() + 暴力反射
│  ├─ 成員方法(Method):getDeclaredMethods()/getMethods() + invoke() + 暴力反射
│  └─ 構造方法(Constructor):getDeclaredConstructors()/getConstructors() + newInstance()
├─ 實戰場景:結合配置文件動態加載
└─ 注意事項:性能、安全、可讀性、兼容性

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

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

相關文章

ARM相關的基礎概念和寄存器

目錄 1、機器碼 2、匯編指令 3、匯編指令集 4、架構 5、內核&#xff08;CPU中的核心&#xff09; 6、處理器 7、復雜指令集和精簡指令集 7.1復雜指令集 7.2精簡指令集 7.3修改 8、內核中的寄存器阻值 8.1溜達存儲類型 8.2、AMR處理器&#xff08;內核&#xff09…

PPT處理控件Aspose.Slides教程:在 C# 中將 PPTX 轉換為 Markdown

將您的PowerPoint幻燈片轉換為Markdown格式&#xff0c;使其兼容 AI 技術。在這個人工智能驅動的時代&#xff0c;GPT和Claude等大模型能夠讀取和生成基于 Markdown 的內容。此外&#xff0c;Markdown 還可用于博客文章和文檔。因此&#xff0c;作為一名 .NET 開發人員&#xf…

Python 多進程(multiprocessing)

文章目錄1. 多進程概述1.1. 多進程的概念1.2. 多進程注意事項2. 進程調用方式2.1. Process 類2.1.1. 構造方法2.1.2. 實例方法2.1.3. 屬性2.2. 面向過程2.3. 面向對象3. 進程間通訊3.1. Queues3.2. Pipes3.3. Managers&#xff08;進行共享數據&#xff09;4. 進程同步5. 進程池…

推薦系統王樹森(五)重排多樣性提升

重排01&#xff1a;物品相似性的度量、提升多樣性的方法_嗶哩嗶哩_bilibili github-PPT 前面的討論中提到 在鏈路的最后進行重排&#xff0c;重排要插入廣告和運營筆記&#xff0c;還要做規則打散&#xff0c;提高推薦的豐富性&#xff0c;比如說一個人特別愛看足球&#xff…

Axios多實例封裝

Axios多實例封裝方案 我將為您提供一個完整的Axios多實例封裝方案&#xff0c;包含基礎封裝、多實例管理和使用示例。 設計思路 創建基礎axios實例封裝&#xff0c;支持請求/響應攔截器實現多實例管理器&#xff0c;支持不同API端點配置提供統一的錯誤處理機制支持請求取消功…

為量化從業者提供免費穩定的股票數據源:免費股票數據API獲取實時數據、歷史數據與技術指標(含多語言代碼指南)實例代碼演示與API文檔說明

? 近年來&#xff0c;股票量化分析因其科學、系統的研究方法&#xff0c;日益成為市場熱點。而進入這一領域的首要步驟&#xff0c;正是獲取全面、準確的股票數據——無論是實時交易行情、歷史走勢記錄&#xff0c;還是企業財務與基本面信息&#xff0c;都是支撐量化策略的核心…

[Sync_ai_vid] UNet模型 | 音頻特征提取器(Whisper)

第2章&#xff1a;LatentSync UNet模型 在第1章&#xff1a;唇形同步推理流程中&#xff0c;我們了解到唇形同步推理流程如同電影導演&#xff0c;協調各"專家模塊"生成完美唇形同步視頻。 本章將深入解析這個"工作室"中最核心的專家——LatentSync UNet…

工業級TF卡NAND + 北京君正 + Rk瑞芯微的應用

目錄前言一&#xff1a;操作CS創世 SD NAND的常用命令1.查看SD設備2.掛載分區3.卸載分區4.分區管理5.格式化分區6.簡單讀寫二&#xff1a;SD底層協議簡要介紹三&#xff1a;對CS創世 SD NAND進行讀寫操作的三大方式1. 使用dd命令2. 使用塊設備的標準接口3. 使用ioctl產生系統調…

深入解析Java并發編程與單例模式

目錄 一、調度(四) 1.隨機調度大環境 二、鎖(二) 1.位置 2.無鎖阻塞 3.重入鎖 4.連續鎖 4.1措施 三、線程方法(二) 1.wait 2.notify 3.wait-notify指位后移 3.1可能時再檢查 3.1.1join(二) 3.1.1.1可能時再檢查死亡 四、單例模式 1.實現 1.1private構造器 1…

java通過redis簡單實現分布式鎖

目錄 一、簡介 二、代碼實現 1、maven添加依賴 2、核心邏輯代碼 3、使用方式 三、總結 一、簡介 我們知道在Java中可以通過關鍵字synchronized來防止并發引起的問題&#xff0c;而分布式中就需要考慮分布式鎖了&#xff0c;今天來講一個比較簡單的實現&#xff0c;通過re…

網絡編程--TCP/UDP Socket套接字

網絡編程 程序員主要操作應用層和傳輸層來實現網絡編程&#xff0c;也就是自己寫一個程序&#xff0c;讓這個程序可以使用網絡來通信&#xff0c;這個程序屬于應用層&#xff0c;實現通訊就需要獲取到傳輸層提供的服務 這就需要使用傳輸層提供的api UDP: 無連接&#xff0c;不可…

claude-code訂閱方案

Claude Code 訂閱方案對比 編寫日期&#xff1a;2025 年 08 月 20 日 &#x1f3f7;? 專業版 Pro ($20/月&#xff0c;$200/年) 主要特性 可用模型&#xff1a;Claude Sonnet 4&#xff08;Claude Opus 4成本太高&#xff0c;謹慎選擇&#xff09;適用場景&#xff1a;適合輕度…

146. LRU緩存

題目&#xff1a; 請你設計并實現一個滿足 LRU (最近最少使用) 緩存 約束的數據結構。 實現 LRUCache 類&#xff1a; LRUCache(int capacity) 以 正整數 作為容量 capacity 初始化 LRU 緩存 int get(int key) 如果關鍵字 key 存在于緩存中&#xff0c;則返回關鍵字的值&#x…

第二十節:3D文本渲染 - 字體幾何體生成與特效

第二十節&#xff1a;3D文本渲染 - 字體幾何體生成與特效 TextGeometry深度解析與高級文字效果實現1. 核心概念解析 1.1 3D文字渲染技術對比技術原理優點缺點TextGeometry將字體輪廓轉換為3D網格真實3D效果&#xff0c;支持材質性能開銷大&#xff0c;內存占用高Canvas紋理將文…

zzz‘sJava知識點概括總結

類型轉化 字符串&#xff1a;c語言&#xff1a;char Java&#xff1a;string 表達式值的類型由最高類型決定&#xff1a; 取值范圍&#xff1a;byte<short<int<long<float<double&#xff08;且運算時byte和short都是轉化為int類型進行計算防止數據溢出&…

SONiC 之 Testbed(2)Ansible

Ansible 是一款由 Red Hat 主導開發的 開源自動化工具&#xff0c;專注于 配置管理、應用部署、任務編排和IT自動化。它基于 無代理&#xff08;Agentless&#xff09;架構&#xff0c;通過 SSH&#xff08;默認&#xff09;或 WinRM 協議與目標設備通信&#xff0c;無需在被控…

瑞芯微RK3568與君正X2600e平臺Linux系統CS創世SD NAND應用全解析與驅動架構詳解

前言 今天就瑞芯微平臺和北京君正平臺下的linux系統中關于CS創世 SD NAND的使用做一些經驗的分享&#xff0c;如有不正&#xff0c;請批評指正&#xff1b; 采用的開發板是RK3568和x2600e&#xff0c;ubuntu版本是20.04&#xff0c;交叉編譯工具鏈是aarch64-linux-gnu-和mips…

深入解析 Flink Function

RichFunctionFunction只是個標記接口public interface Function extends java.io.Serializable {}RichFunction 的核心語義是為用戶定義的函數&#xff08;UDF&#xff09;提供生命周期管理和運行時上下文訪問的能力。任何一個普通的 Flink Function 接口&#xff08;例如 MapF…

JMeter —— 壓力測試

目錄 常用的性能指標 一、吞吐量類指標 二、響應時間類指標 三、資源利用率指標 JMeter 一、JMeter 簡介 二.下載安裝JMeter&#xff1a; 三.如何使用JMeter&#xff1a; 壓力測試考察當前軟硬件環境下系統所能承受的最大負荷并幫助找出系統瓶頸所在。壓測都是為了系統…

Transformer在哪?做了權重共享?

1、什么是權值共享權重共享是指在模型的不同層之間復?相同的參數。這可以減少模型的總體參數數量&#xff0c;并使得模型在訓練時更容易學習。2、在Transformer中的應用常見的做法是共享詞嵌入層&#xff08;embedding layer&#xff09;和輸出層&#xff08;output layer&…