首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 反射深度測試:建構物件、呼叫私有方法、存取私有欄位完整實戰

     編輯:Java 相關知識

Java 反射機制允許運行時動態檢查類別結構、建構實例、呼叫方法、存取欄位,甚至突破封裝訪問私有成員。本文透過完整程式碼示範反射核心 API 的實戰操作與輸出驗證。

一、測試目標類別:MyService

package lavasoft.test;

public class MyService {
    private String msg;

    public MyService() {
        System.out.println("log: 無參建構子被呼叫!");
    }
    public MyService(String msg) {
        System.out.println("log: 有參建構子被呼叫!");
        this.msg = msg;
    }
    public String doSomething(String person, String something) {
        System.out.println("log: 業務方法 doSomething 被呼叫!");
        return person + " is doing " + something + "! 附加訊息:" + msg;
    }
    public String getMsg() {
        System.out.println("log: getMsg 被呼叫!");
        return msg;
    }
    public void setMsg(String msg) {
        System.out.println("log: setMsg 被呼叫!");
        this.msg = msg;
    }
    private String doPrivate() {
        System.out.println("log: 私有方法 doPrivate 被呼叫!");
        return "私有方法 doPrivate() 被呼叫了!";
    }
}

二、反射測試主程式:TestReflect

package lavasoft.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Field;

public class TestReflect {
    public static void main(String[] args) throws Exception {
        // 1. 取得 Class 物件
        Class<MyService> clazz = MyService.class;

        // 2. 反射建構物件 (無參建構子)
        MyService sv = clazz.getConstructor().newInstance();
        sv.setMsg("透過 Class 方式建立物件,並呼叫物件上的方法!");

        // 3. 反射呼叫 public 方法
        Method mSetMsg = clazz.getMethod("setMsg", String.class);
        mSetMsg.invoke(sv, "透過反射呼叫了 sv 物件上的方法!");

        Method mDo = clazz.getMethod("doSomething", String.class, String.class);
        String result = (String) mDo.invoke(sv, "張三", "吃飯");
        System.out.println(result);

        // 4. 反射呼叫 private 方法
        Method mDoPrivate = clazz.getDeclaredMethod("doPrivate");
        mDoPrivate.setAccessible(true);  // 關鍵:暴力反射破解封裝
        mDoPrivate.invoke(sv);

        // 5. 反射存取 private 欄位
        Field fMsg = clazz.getDeclaredField("msg");
        fMsg.setAccessible(true);        // 關鍵:暴力反射破解封裝
        fMsg.set(sv, "hahahahahahahaha");
        System.out.println(sv.getMsg());
    }
}

三、關鍵 API 對照表

操作方法備註
取得 public 建構子clazz.getConstructor(參數型別...)拋 NoSuchMethodException
取得任意建構子clazz.getDeclaredConstructor(參數型別...)含 private,需 setAccessible(true)
建構實例constructor.newInstance(參數...)拋 InstantiationException/InvocationTargetException
取得 public 方法clazz.getMethod(名稱, 參數型別...)含繼承自父類的 public 方法
取得任意方法clazz.getDeclaredMethod(名稱, 參數型別...)含 private,不含繼承,需 setAccessible(true)
呼叫方法method.invoke(實例, 參數...)靜態方法實例傳 null
取得 public 欄位clazz.getField(名稱)含繼承
取得任意欄位clazz.getDeclaredField(名稱)含 private,不含繼承,需 setAccessible(true)
設定欄位值field.set(實例, 值)靜態欄位實例傳 null
讀取欄位值field.get(實例)

四、執行輸出驗證

log: 無參建構子被呼叫!
log: setMsg 被呼叫!
log: setMsg 被呼叫!
log: 業務方法 doSomething 被呼叫!
張三 is doing 吃飯! 附加訊息:透過反射呼叫了 sv 物件上的方法!
log: 私有方法 doPrivate 被呼叫了!
log: getMsg 被呼叫!
hahahahahahahaha
Process finished with exit code 0

五、進階應用場景

  • 框架開發:Spring/MyBatis 透過反射實現依賴注入、ORM 映射
  • 測試工具:JUnit/TestNG 反射發現 @Test 方法、執行私有測試方法
  • 序列化庫:Jackson/Gson 反射讀寫私有欄位
  • 熱重載/動態代理:運行時載入新類別版本、建構代理物件
  • 注解處理:編譯期/運行期掃描類別/方法/欄位註解

六、效能與安全考量

考量點說明優化建議
效能開銷反射比直接呼叫慢 10-100 倍 (JIT 優化後縮小)緩存 Constructor/Method/Field 物件、使用 MethodHandle (Java 7+)
封裝破壞setAccessible(true) 可繞過 private/protectedSecurityManager 限制、模組化 (Java 9+) 強制封裝
異常處理檢查型異常多 (InvocationTargetException 等)統一包裝為非檢查異常、使用 Lombok @SneakyThrows
泛型擦除運行時無法取得泛型參數實際類型使用 TypeToken (Gson) 或 ParameterizedType 解析

七、Java 9+ 模組系統影響

// Java 9+ 需在 module-info.java 開放反射存取
module lavasoft.test {
    opens lavasoft.test to java.base, java.lang.reflect;  // 允許反射存取私有成員
    // 或 opens lavasoft.test;  // 開放給所有模組
}

未開放時 setAccessible(true) 會拋 InaccessibleObjectException。

八、最佳實踐原則

  1. 能不用反射就不用:介面多型、策略模式、工廠模式優先
  2. 緩存反射物件:Class、Method、Field、Constructor 都是執行緒安全、可重用
  3. 限制範圍:僅在框架基礎設施代碼使用,業務代碼禁止
  4. 處理 InvocationTargetException:真實異常在 getCause() 中
  5. 考慮 MethodHandle/VarHandle:Java 7/9+ 提供更高效、類型安全的替代方案
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved