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

Java 反射機制深度解析:Class、Method、Field、Constructor 全方位實戰

     編輯:Java 相關知識

反射核心價值

Java 反射允許運行時檢查類結構、動態創建對象、調用方法/訪問字段、加載未知類。它是 Spring、Hibernate、MyBatis、Gson、JUnit 等框架的基石。核心類位於 java.lang.reflect 包。

三步走反射標準流程

  1. 獲取 Class<?> 對象
  2. 通過 Class 獲取 Constructor、Method、Field
  3. 調用 newInstance()、invoke()、get()/set() 執行操作

獲取 Class 對象的三種方式


Class clazz1 = String.class;                    // 1. 類字面量 (編譯期已知,不初始化類)
Class clazz2 = "hello".getClass();              // 2. 實例 getClass() (運行期獲取實際類型)
Class clazz3 = Class.forName("java.lang.String"); // 3. 字符串名稱 (會初始化類,拋 ClassNotFoundException)

完整實戰示例:動態調用、屬性拷貝、創建實例

1. 動態調用方法


import java.lang.reflect.Method;

public class ReflectionInvokeDemo {
    public static void main(String[] args) throws Exception {
        // 目標對象
        Printer printer = new Printer();
        
        // 1. 獲取 Class
        Class clazz = printer.getClass(); // 或 Printer.class
        
        // 2. 獲取 Method (方法名 + 參數類型)
        Method printMethod = clazz.getMethod("print", String.class);
        
        // 3. 調用 (對象實例 + 實參)
        printMethod.invoke(printer, "Hello Reflection!");
        // 輸出: Printing: Hello Reflection!
    }
}

class Printer {
    public void print(String msg) {
        System.out.println("Printing: " + msg);
    }
}

2. 動態訪問/修改私有字段


import java.lang.reflect.Field;

public class ReflectionFieldDemo {
    public static void main(String[] args) throws Exception {
        User user = new User("Alice", 25);
        
        Class clazz = user.getClass();
        
        // 獲取聲明字段 (包含 private,不含繼承)
        Field nameField = clazz.getDeclaredField("name");
        Field ageField = clazz.getDeclaredField("age");
        
        // 關鍵: 破壞封裝,允許訪問私有成員
        nameField.setAccessible(true);
        ageField.setAccessible(true);
        
        // 讀取
        System.out.println("原始: " + nameField.get(user) + ", " + ageField.get(user));
        
        // 修改
        nameField.set(user, "Bob");
        ageField.set(user, 30);
        
        System.out.println("修改後: " + user); // User{name='Bob', age=30}
    }
}

class User {
    private String name;
    private int age;
    public User(String name, int age) { this.name = name; this.age = age; }
    @Override public String toString() { return "User{name='" + name + "', age=" + age + "}"; }
}

3. 動態創建實例 (含帶參構造器)


import java.lang.reflect.Constructor;

public class ReflectionNewInstanceDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = User.class;
        
        // 無參構造器 (要求類有 public 無參構造器)
        User u1 = clazz.getDeclaredConstructor().newInstance();
        // 或 clazz.newInstance() (JDK 9 棄用)
        
        // 帶參構造器
        Constructor ctor = clazz.getDeclaredConstructor(String.class, int.class);
        ctor.setAccessible(true); // 若構造器非 public
        User u2 = ctor.newInstance("Charlie", 28);
        
        System.out.println(u1); // User{name='null', age=0}
        System.out.println(u2); // User{name='Charlie', age=28}
    }
}

4. 通用對象屬性拷貝工具 (BeanUtils 核心邏輯)


import java.lang.reflect.Field;
import java.util.Arrays;

public class BeanCopier {
    public static void copyProperties(Object source, Object target) throws Exception {
        Class srcClass = source.getClass();
        Class tgtClass = target.getClass();
        
        // 獲取源對象所有聲明字段
        Field[] srcFields = srcClass.getDeclaredFields();
        // 批量設置可訪問 (性能優於逐個 setAccessible)
        java.lang.reflect.AccessibleObject.setAccessible(srcFields, true);
        
        for (Field srcField : srcFields) {
            String name = srcField.getName();
            try {
                // 目標對象查找同名字段
                Field tgtField = tgtClass.getDeclaredField(name);
                tgtField.setAccessible(true);
                
                // 類型兼容檢查 (簡化版)
                if (tgtField.getType().isAssignableFrom(srcField.getType())) {
                    Object value = srcField.get(source);
                    tgtField.set(target, value);
                }
            } catch (NoSuchFieldException ignored) {
                // 目標無此字段,跳過
            }
        }
    }

    public static void main(String[] args) throws Exception {
        User src = new User("Source", 99);
        User tgt = new User("Target", 0);
        copyProperties(src, tgt);
        System.out.println(tgt); // User{name='Source', age=99}
    }
}

關鍵 API 對比表

方法返回範圍訪問權限
getMethod(name, params)Methodpublic (含繼承)僅 public
getDeclaredMethod(name, params)Method本類聲明 (含私有)需 setAccessible(true)
getFields()Field[]public (含繼承)僅 public
getDeclaredFields()Field[]本類聲明 (含私有)需 setAccessible(true)
getConstructors()Constructor[]public僅 public
getDeclaredConstructors()Constructor[]本類聲明 (含私有)需 setAccessible(true)

性能與安全考量

⚠️ 反射性能開銷:
  • 方法調用比直接調用慢 10-100 倍 (JIT 難優化、訪問檢查、參數裝箱)。
  • 熱點路徑緩存 Method/Field/Constructor 對象 (它們是不可變線程安全的)。
  • JDK 7+ MethodHandle、JDK 9+ VarHandle 提供更接近直接調用的性能。
🔒 安全風險:
  • setAccessible(true) 破壞封裝,可能被安全管理器禁止。
  • 模塊化 (JDK 9+) 需 opens package to module 導出才能反射私有成員。

現代替代方案

  • MethodHandle / VarHandle:更底層、可組合、性能更好,適合框架開發者。
  • Annotation Processing (APT):編譯期生成代碼,運行期零反射 (如 Dagger, Room, MapStruct)。
  • GraalVM Native Image:需配置反射註冊表 reflect-config.json,否則運行期報錯。

總結

Java 反射是框架級能力,業務代碼慎用。掌握 Class 獲取、Method/Field/Constructor 核心三件套、setAccessible(true) 破壞封裝、泛型擦除下的 Type 體系補完,能讓你讀懂主流框架源碼、編寫通用工具類、實現插件化架構。記住:緩存反射對象、避開熱點路徑、拥抱編譯期代碼生成。

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