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

Java 反射實戰:獲取類別成員屬性與建構子完整指南

     編輯:Java 綜合教學

核心 API 對照

目標Class 方法返回類型說明
所有 public 成員(含繼承)getFields()Field[]僅 public,包含父類
本類宣告的所有成員getDeclaredFields()Field[]包含 private/protected,不含繼承
所有 public 建構子getConstructors()Constructor[]僅 public
本類宣告的所有建構子getDeclaredConstructors()Constructor[]包含 private/protected

完整程式碼範例

package com.example.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectDemo {

    public static void main(String[] args) {
        // 1. 獲取 Class 物件的三種方式
        Class<?> clazz1 = String.class;           // 方式一:類字面常量
        Class<?> clazz2 = "hello".getClass();      // 方式二:實例.getClass()
        Class<?> clazz3 = null;
        try {
            clazz3 = Class.forName("java.lang.String"); // 方式三:完整類名
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 2. 列印類別基本資訊
        printClassInfo(clazz1);

        // 3. 列印所有宣告的成員屬性
        printDeclaredFields(clazz1);

        // 4. 列印所有宣告的建構子
        printDeclaredConstructors(clazz1);

        // 5. 實戰:動態建立實例並存取私有欄位
        demonstratePrivateAccess();
    }

    /** 列印類別基本資訊 */
    static void printClassInfo(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().getName());
        System.out.println("實作介面:" + java.util.Arrays.toString(clazz.getInterfaces()));
        System.out.println("是否為介面:" + clazz.isInterface());
        System.out.println("是否為陣列:" + clazz.isArray());
        System.out.println("是否為基本類型:" + clazz.isPrimitive());
        System.out.println("是否為枚舉:" + clazz.isEnum());
        System.out.println("是否為註解:" + clazz.isAnnotation());
    }

    /** 列印宣告的成員屬性 */
    static void printDeclaredFields(Class<?> clazz) {
        System.out.println("\n========== 宣告的成員屬性 ==========");
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String modifiers = Modifier.toString(field.getModifiers());
            String typeName = field.getType().getSimpleName();
            String fieldName = field.getName();
            System.out.printf("%-15s %-20s %s%n", modifiers, typeName, fieldName);
        }
    }

    /** 列印宣告的建構子 */
    static void printDeclaredConstructors(Class<?> clazz) {
        System.out.println("\n========== 宣告的建構子 ==========");
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> ctor : constructors) {
            String modifiers = Modifier.toString(ctor.getModifiers());
            String paramTypes = java.util.Arrays.stream(ctor.getParameterTypes())
                    .map(Class::getSimpleName)
                    .collect(java.util.stream.Collectors.joining(", "));
            System.out.printf("%-15s %s(%s)%n", modifiers, clazz.getSimpleName(), paramTypes);
        }
    }

    /** 示範存取私有欄位與呼叫私有建構子 */
    static void demonstratePrivateAccess() {
        System.out.println("\n========== 私有存取示範 ==========");
        try {
            Class<?> clazz = Class.forName("java.lang.String");

            // 1. 獲取私有建構子並建立實例
            Constructor<?> ctor = clazz.getDeclaredConstructor(char[].class, boolean.class);
            ctor.setAccessible(true); // 關鍵:暴力反射破壞封裝
            Object strObj = ctor.newInstance(new char[]{'H', 'i'}, true);
            System.out.println("透過私有建構子建立:" + strObj);

            // 2. 存取私有欄位 value (String 內部 char[] 欄位,JDK 9+ 為 byte[])
            Field valueField = clazz.getDeclaredField("value");
            valueField.setAccessible(true);
            Object value = valueField.get(strObj);
            System.out.println("私有欄位 value 內容:" + java.util.Arrays.toString((Object[]) value));

        } catch (Exception e) {
            System.err.println("反射操作失敗:" + e.getMessage());
        }
    }
}

輸出結果範例

========== 類別資訊 ==========
完整類名:java.lang.String
簡單類名:String
包名:java.lang
修飾符:public final
父類別:java.lang.Object
實作介面:[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
是否為介面:false
是否為陣列:false
是否為基本類型:false
是否為枚舉:false
是否為註解:false

========== 宣告的成員屬性 ==========
final            byte[]               value
static           Comparator           CASE_INSENSITIVE_ORDER
private static   long                 serialVersionUID
private          int                  hash
private static   ObjectStreamField[] serialPersistentFields

========== 宣告的建構子 ==========
public           String(char[], boolean)
public           String(byte[], int, int, java.nio.charset.Charset)
public           String(byte[], int, int)
... (共 15 個建構子)

========== 私有存取示範 ==========
透過私有建構子建立:Hi
私有欄位 value 內容:[H, i]

關鍵技術點解析

1. setAccessible(true) - 暴力反射

  • 繞過 Java 語言存取檢查,存取 private、protected、package-private 成員。
  • 需確保 SecurityManager 允許 suppressAccessChecks 權限(生產環境通常關閉 SecurityManager)。
  • JDK 9+ 模組系統需在 module-info.java 開放 opens com.example to java.base 或命令列 --add-opens。

2. 建構子參數類型匹配

// 精確匹配參數類型順序
Constructor<?> ctor = clazz.getDeclaredConstructor(String.class, int.class, boolean.class);

3. 陣列與集合的泛型資訊擦除

反射無法直接獲取執行期泛型參數(如 List<String> 的 String),需透過 Type、ParameterizedType 解析簽名:

Field field = clazz.getDeclaredField("stringList");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType) genericType;
    Type[] actualTypes = pt.getActualTypeArguments(); // [class java.lang.String]
}

4. 效能考量

  • 反射比直接呼叫慢 10-100 倍(JIT 難以優化、存取檢查、自動裝箱)。
  • 高頻呼叫場景:緩存 Field/Method/Constructor 物件,或使用 MethodHandle/VarHandle(JDK 7/9+)。
  • 框架級開發(Spring、Hibernate、Jackson)大量使用反射,但會進行深度優化(快取、代碼生成)。

實戰應用場景

  1. 框架開發:依賴注入容器、ORM 映射、序列化庫。
  2. 測試工具:單元測試存取私有狀態、Mock 靜態方法。
  3. 動態代理:JDK Proxy、CGLIB、ByteBuddy 生成代理類。
  4. 配置綁定:YAML/Properties 映射到 POJO(@ConfigurationProperties)。
  5. 插件系統:動態載入外部 JAR、掃描註解發現擴充點。

安全與模組化注意事項 (JDK 9+)

  • 內部 API(sun.misc.*、com.sun.*)預設封裝,需 --add-exports 開放。
  • 自定義模組需在 module-info.java 宣告 opens 允許反射:
module com.myapp {
    opens com.myapp.model to java.base, spring.core;
    // 或無條件開放
    opens com.myapp.model;
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved