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

Java 反射機制完整指南:獲取類資訊、動態實例化、方法調用與泛型擷取

     編輯:Java 相關知識

反射是 Java 動態性的基石,框架(Spring、Hibernate、MyBatis)底層皆大量依賴反射。本文系統講解 **獲取 Class 對象四種方式、類結構資訊擷取(欄位/方法/建構子/註解/泛型)、動態建立實例、動態調用方法、訪問私有成員、效能考量與安全限制**,附完整工具類範例。

一、獲取 Class 對象四種途徑


Class c1 = String.class;                    // 1. 類字面量 (編譯期已知)
Class c2 = "hello".getClass();              // 2. 實例 getClass()
Class c3 = Class.forName("java.lang.String"); // 3. 字串全限定名 (動態載入)
Class c4 = int.class;                       // 4. 基本型別 .class
Class c5 = Integer.TYPE;                    // 基本型別包裝類 .TYPE

二、類結構資訊完整擷取


public class ReflectionInspector {
    public static void inspect(Class clazz) {
        System.out.println("=== 類基本資訊 ===");
        System.out.println("完整名稱: " + clazz.getName());
        System.out.println("簡單名稱: " + clazz.getSimpleName());
        System.out.println("包名: " + clazz.getPackageName());
        System.out.println("修飾符: " + Modifier.toString(clazz.getModifiers()));
        System.out.println("父類: " + (clazz.getSuperclass() != null ? clazz.getSuperclass().getName() : "無"));
        System.out.println("介面: " + Arrays.toString(clazz.getInterfaces()));

        System.out.println("\n=== 建構子 ===");
        for (Constructor ctor : clazz.getDeclaredConstructors()) {
            System.out.println(Modifier.toString(ctor.getModifiers()) + " " + ctor.getName() + Arrays.toString(ctor.getParameterTypes()));
        }

        System.out.println("\n=== 成員欄位 (含私有) ===");
        for (Field f : clazz.getDeclaredFields()) {
            System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getType().getSimpleName() + " " + f.getName());
        }

        System.out.println("\n=== 成員方法 (含私有) ===");
        for (Method m : clazz.getDeclaredMethods()) {
            System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName() + Arrays.toString(m.getParameterTypes()));
        }

        System.out.println("\n=== 註解 ===");
        for (Annotation ann : clazz.getDeclaredAnnotations()) {
            System.out.println(ann);
        }
    }
}

三、動態實例化與方法調用

1. 調用建構子建立實例


Constructor ctor = clazz.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true); // 繞過 private
Object obj = ctor.newInstance("Tom", 18);

2. 調用實例方法


Method method = clazz.getDeclaredMethod("sayHello", String.class);
method.setAccessible(true);
Object result = method.invoke(obj, "World"); // 非 static 傳實例

3. 調用靜態方法


Method staticMethod = clazz.getDeclaredMethod("staticHelper", int.class);
staticMethod.setAccessible(true);
Object result = staticMethod.invoke(null, 100); // static 傳 null

4. 讀寫欄位值


Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(obj, "Jerry"); // 寫入
String val = (String) field.get(obj); // 讀取

四、泛型資訊擷取 (核心進階)

Java 泛型擦除後僅保留原始類型,但**方法簽名、欄位宣告、父類泛型參數**的泛型資訊保留在 Class 中,可通過 Type 系統擷取:


public class GenericResolver {
    // 解析欄位實際泛型類型
    public static Class getFieldGenericType(Field field) {
        Type type = field.getGenericType();
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            Type actualType = pt.getActualTypeArguments()[0];
            return (Class) actualType;
        }
        return (Class) type;
    }

    // 解析方法回傳值泛型
    public static Class getMethodReturnGenericType(Method method) {
        Type type = method.getGenericReturnType();
        if (type instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
        }
        return (Class) type;
    }
}

五、效能與安全考量

面向說明對策
效能反射調用比直接調用慢 10~100 倍 (JIT 難優化、存取檢查、裝箱/拆箱)緩存 Class/Method/Field 對象;熱點路徑用 MethodHandle/VarHandle (Java 9+) 或代碼生成 (ASM/ByteBuddy)
安全setAccessible(true) 可能被 SecurityManager 禁止 (模組化後更嚴格)模組系統中需在 module-info.java opens 對應包;或啟動參數 --add-opens
封裝破壞可隨意讀寫 private 成員僅在框架/測試/序列化等必要場景使用,業務代碼禁用

六、實用工具類:ReflectUtils


public final class ReflectUtils {
    public static <T> T newInstance(Class<T> clazz, Object... args) {
        try {
            Constructor<T> ctor = findConstructor(clazz, args);
            ctor.setAccessible(true);
            return ctor.newInstance(args);
        } catch (Exception e) { throw new RuntimeException(e); }
    }

    public static Object invokeMethod(Object target, String methodName, Object... args) {
        try {
            Method method = findMethod(target.getClass(), methodName, args);
            method.setAccessible(true);
            return method.invoke(target, args);
        } catch (Exception e) { throw new RuntimeException(e); }
    }

    public static void setField(Object target, String fieldName, Object value) {
        try {
            Field field = target.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(target, value);
        } catch (Exception e) { throw new RuntimeException(e); }
    }

    public static Object getField(Object target, String fieldName) {
        try {
            Field field = target.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(target);
        } catch (Exception e) { throw new RuntimeException(e); }
    }

    private static Constructor findConstructor(Class clazz, Object[] args) {
        for (Constructor ctor : clazz.getDeclaredConstructors()) {
            if (matches(ctor.getParameterTypes(), args)) return ctor;
        }
        throw new NoSuchMethodException("No matching constructor");
    }

    private static Method findMethod(Class clazz, String name, Object[] args) {
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.getName().equals(name) && matches(m.getParameterTypes(), args)) return m;
        }
        throw new NoSuchMethodException("No matching method: " + name);
    }

    private static boolean matches(Class[] paramTypes, Object[] args) {
        if (paramTypes.length != args.length) return false;
        for (int i = 0; i < paramTypes.length; i++) {
            if (args[i] != null && !paramTypes[i].isAssignableFrom(args[i].getClass())) return false;
        }
        return true;
    }
}

掌握反射核心 API 與泛型擷取技巧,是閱讀框架源碼、構建通用組件的必備能力。

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