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

Java 反射機制全方位深度解析:從 Class 到泛型

     編輯:Java 相關知識

反射核心價值

Java 反射讓我們能在 運行時 獲取類的完整信息並動態操作,打破了編譯期的靜態限制。核心能力包括:

  • 獲取 Class 對象及類元數據
  • 動態創建實例、調用方法、訪問字段
  • 處理泛型類型擦除後的參數化信息
  • 操作註解、數組等進階特性

實驗用測試類

public abstract class FatherObject implements Runnable {
    public void doSomething() { System.out.println("做事情......"); }
}

public class ExampleObject extends FatherObject {
    public int age = 30;
    public String name = "byhieg";
    private Integer score = 60;
    
    // 構造器、Getter/Setter、方法重寫等...
}

一、獲取 Class 對象:反射的入口

// 方式一:編譯期已知類名
Class<?> clazz = ExampleObject.class;

// 方式二:運行期通過字符串獲取(需全限定名,且在 classpath 下)
Class<?> clazz = Class.forName("cn.byhieg.reflectiontutorial.ExampleObject");

二、類基本信息獲取

// 類名
String fullName = clazz.getName();        // cn.byhieg.reflectiontutorial.ExampleObject
String simpleName = clazz.getSimpleName(); // ExampleObject

// 包信息
Package pkg = clazz.getPackage();

// 父類
Class<?> superClass = clazz.getSuperclass(); // FatherObject

// 實現的接口
Class<?>[] interfaces = superClass.getInterfaces(); // [Runnable]

// 修飾符判斷
Modifier.isAbstract(superClass.getModifiers()); // true

三、構造器操作

獲取所有 public 構造器

Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> c : constructors) {
    System.out.println(c); // 打印構造器簽名
    // 獲取參數類型
    for (Class<?> param : c.getParameterTypes()) {
        System.out.println("參數: " + param);
    }
}

獲取指定構造器並實例化

// 指定參數類型獲取
Constructor<?> ctor = clazz.getConstructor(String.class);

// 實例化(需拋出異常)
Object obj = ctor.newInstance("byhieg");

// 無參構造器快捷方式
Object obj2 = clazz.newInstance(); // 僅適用無參構造器

四、字段操作

獲取 public 字段

Field[] fields = clazz.getFields(); // 僅 public
for (Field f : fields) {
    System.out.println(f.getName() + ": " + f.getType());
}

獲取指定字段並動態賦值

Field ageField = clazz.getField("age");
Object obj = clazz.newInstance();
System.out.println("原值: " + ageField.get(obj)); // 30
ageField.set(obj, 25);
System.out.println("新值: " + ageField.get(obj)); // 25

訪問 private 字段(需暴力反射)

Field scoreField = clazz.getDeclaredField("score");
scoreField.setAccessible(true); // 關鍵:暴力反射
scoreField.set(obj, 100);

五、方法操作

獲取並調用方法

Method setAge = clazz.getMethod("setAge", int.class);
setAge.invoke(obj, 28); // 相當於 obj.setAge(28)

// 獲取方法信息
System.out.println("方法名: " + setAge.getName());
System.out.println("返回類型: " + setAge.getReturnType().getName()); // void
for (Class<?> param : setAge.getParameterTypes()) {
    System.out.println("參數: " + param.getName());
}

獲取所有方法(含繼承)vs 僅當前類聲明

clazz.getMethods();        // 所有 public 方法(含父類、Object)
clazz.getDeclaredMethods(); // 當前類聲明的所有方法(含 private)

六、註解處理

自定義註解

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String name();
    String value();
}

@MyAnnotation(name="class", value="hello")
public class AnnotationObject {
    @MyAnnotation(name="field", value="欄位")
    public String field;
    
    @MyAnnotation(name="method", value="方法")
    public void doSomething() {}
    
    public void doOther(@MyAnnotation(name="param", value="參數") String param) {}
}

運行時讀取註解

// 類註解
Annotation[] annos = clazz.getAnnotations();
for (Annotation a : annos) {
    if (a instanceof MyAnnotation) {
        MyAnnotation ma = (MyAnnotation) a;
        System.out.println(ma.name() + "=" + ma.value());
    }
}

// 方法參數註解(二維數組:每個參數可能有多個註解)
Method method = clazz.getMethod("doOther", String.class);
Annotation[][] paramAnnos = method.getParameterAnnotations();
Class<?>[] paramTypes = method.getParameterTypes();

for (int i = 0; i < paramAnnos.length; i++) {
    System.out.println("參數 " + i + " (" + paramTypes[i].getName() + "):");
    for (Annotation a : paramAnnos[i]) {
        if (a instanceof MyAnnotation) {
            System.out.println("  " + ((MyAnnotation)a).name() + "=" + ((MyAnnotation)a).value());
        }
    }
}

七、泛型類型擦除後的參數化類型獲取

由於 Java 泛型擦除,運行時無法直接獲取 List<String> 中的 String,但可通過反射繞過:

場景一:方法返回泛型

Method getLists = clazz.getMethod("getLists");
Type genericReturn = getLists.getGenericReturnType();

if (genericReturn instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType) genericReturn;
    for (Type t : pt.getActualTypeArguments()) {
        System.out.println("參數化類型: " + ((Class<?>)t).getName()); // java.lang.String
    }
}

場景二:方法參數為泛型

Method setLists = clazz.getMethod("setLists", List.class);
Type[] genericParams = setLists.getGenericParameterTypes();

for (Type gp : genericParams) {
    if (gp instanceof ParameterizedType) {
        ParameterizedType pt = (ParameterizedType) gp;
        for (Type t : pt.getActualTypeArguments()) {
            System.out.println("參數化類型: " + ((Class<?>)t).getName());
        }
    }
}

場景三:字段為泛型

Field listsField = clazz.getField("lists");
Type fieldType = listsField.getGenericType();
// 後續處理同 ParameterizedType

八、數組操作

動態創建並操作數組

// 創建 int[3]
int[] arr = (int[]) Array.newInstance(int.class, 3);

// 賦值
for (int i = 0; i < arr.length; i++) {
    Array.set(arr, i, i + 10);
}

// 取值
for (int i = 0; i < arr.length; i++) {
    System.out.println(Array.get(arr, i));
}

獲取數組 Class 對象

// 基本類型數組
Class<?> intArrayClass = Class.forName("[I"); // int[]
Class<?> compType = intArrayClass.getComponentType(); // int

// 引用類型數組
Class<?> strArrayClass = Class.forName("[Ljava.lang.String;"); // String[]

總結

反射是 Java 動態性的基石,框架開發(Spring、Hibernate、MyBatis 等)大量依賴反射。掌握反射能讓你:

  • 理解主流框架核心原理
  • 編寫通用工具類(JSON 序列化、ORM 映射等)
  • 實現插件化、熱加載等高級架構

注意:反射有性能開銷(JIT 優化較難)、破壞封裝性、編譯期無法檢查錯誤,業務代碼中應謹慎使用。

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