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

Java 反射機制動態調用方法完整指南:靜態/非靜態、公有/私有

     編輯:Java 程式設計入門

前言

Java 反射允許運行時動態獲取類信息並調用方法,打破了編譯期綁定的限制。本文通過實例演示如何使用反射調用 Math.sin()(靜態方法)和 String.equals()(非靜態方法),並解析核心 API 與常見坑點。

核心 API 速查

類/方法用途
Class.forName("全限定名")加載類對象
clazz.getDeclaredMethod(name, paramTypes)獲取聲明的方法(含 private)
clazz.getMethod(name, paramTypes)獲取 public 方法(含繼承)
method.invoke(obj, args)調用方法,靜態方法 obj 為 null
method.setAccessible(true)暴力反射,繞過訪問修飾符檢查

完整代碼示例

import java.lang.reflect.Method;

public class DynamicInvocationDemo {

    public static void main(String[] args) {
        try {
            // ========== 1. 調用靜態方法:Math.sin(double) ==========
            System.out.println("=== 調用 Math.sin() 靜態方法 ===");
            
            // 獲取 Method 對象:參數類型用 Double.TYPE 表示 double 基本類型
            Method sinMethod = Math.class.getDeclaredMethod("sin", double.class);
            
            // 靜態方法調用:第一個參數傳 null(無實例)
            Double result = (Double) sinMethod.invoke(null, 1.0); // 也可傳 1,自動裝箱
            System.out.printf("sin(1.0) = %.4f%n", result); // 輸出: 0.8415
            
            // ========== 2. 調用非靜態方法:String.equals(Object) ==========
            System.out.println("\n=== 調用 String.equals() 實例方法 ===");
            
            Method equalsMethod = String.class.getDeclaredMethod("equals", Object.class);
            
            String target = "明日科技";
            String instance = new String("明日科技"); // 新對象,非字面量
            
            // 非靜態方法調用:第一個參數為調用實例
            Boolean equal = (Boolean) equalsMethod.invoke(instance, target);
            System.out.println("字符串比較結果:" + equal); // true
            
            // ========== 3. 進階:調用私有方法 ==========
            System.out.println("\n=== 反射調用私有方法演示 ===");
            invokePrivateMethod();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** 定義一個包含私有方法的測試類 */
    static class Service {
        private String secret(String prefix, int id) {
            return prefix + "-SECRET-" + id;
        }
    }

    private static void invokePrivateMethod() throws Exception {
        Service service = new Service();
        Class clazz = service.getClass();
        
        // 獲取私有方法
        Method secretMethod = clazz.getDeclaredMethod("secret", String.class, int.class);
        
        // 關鍵:暴力反射打破封裝
        secretMethod.setAccessible(true);
        
        // 調用
        String result = (String) secretMethod.invoke(service, "TOKEN", 100);
        System.out.println("私有方法返回:" + result); // TOKEN-SECRET-100
    }
}

關鍵細節深度解析

1. 參數類型表示

  • 基本類型:int.class / Integer.TYPE、double.class / Double.TYPE 等
  • 引用類型:String.class、Object.class、List.class 等
  • 數組類型:int[].class、String[].class

2. 靜態 vs 非靜態調用差異

// 靜態方法
method.invoke(null, args...);  // 第一參數忽略,傳 null

// 實例方法
method.invoke(instance, args...); // 第一參數必須是目標對象實例

3. 異常處理

invoke() 拋出的 InvocationTargetException 包裝了真實異常,需通過 e.getCause() 獲取:

try {
    method.invoke(obj, args);
} catch (InvocationTargetException e) {
    Throwable realCause = e.getCause(); // 真實業務異常
    // 處理 realCause...
}

4. 性能考量

  • 反射調用比直接調用慢 10-100 倍(JIT 優化後差距縮小)
  • 熱點代碼避免頻繁反射,可緩存 Method 對象
  • JDK 7+ 引入 MethodHandle、JDK 8+ VarHandle 性能更優

實戰應用場景

  1. 框架開發:Spring @Autowired、MyBatis Mapper 接口代理
  2. 插件系統:動態加載第三方 Jar,調用約定接口
  3. 測試工具:JUnit/TestNG 運行測試方法
  4. 序列化庫:Jackson/Gson 獲取字段/方法
  5. 熱更新:運行時替換類實現邏輯

安全風險與最佳實踐

  • ⚠️ setAccessible(true) 破壞封裝,可能導致安全漏洞,生產環境謹慎使用
  • ⚠️ 模塊化系統(JPMS/JDK 9+)默認禁止跨模塊反射,需在 module-info.java 中 opens 包
  • ✅ 緩存 Method 對象避免重複 getDeclaredMethod 開銷
  • ✅ 優先使用標準 API,反射作為最後手段

總結

反射是 Java 動態性的基石,掌握 Class → Method → invoke 鏈路是核心。本文示範了靜態/實例、公有/私有方法的調用全流程,理解參數類型表示、異常解包、性能權衡,即可在框架開發、工具構建中靈活運用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved