反射是 Java 動態性的基石,允許運行時檢測類結構、創建實例、調用方法。本文系統梳理核心 API、三種類加載方式、屬性訪問差異及優缺點權衡。
| 元素 | 反射類 | 說明 |
|---|---|---|
| 類文件 | java.lang.Class | 描述內存中 .class 文件結構 |
| 屬性 | java.lang.reflect.Field | 描述字段(含修飾符、類型、名稱) |
| 構造方法 | java.lang.reflect.Constructor | 描述構造器參數、異常、修飾符 |
| 普通方法 | java.lang.reflect.Method | 描述方法簽名、返回類型、異常 |
Class.forName("全限定類名"):最常用,支持動態配置。類名.class:編譯期已知類型,無異常拋出。對象.getClass():運行時從實例獲取。getFields():僅獲取 public 屬性(含繼承自父類的)。getDeclaredFields():獲取本類聲明的 所有 屬性(含 private、protected、default),不含繼承。| 優點 | 缺點 |
|---|---|
| 運行時動態獲取類信息,極大提升靈活性與擴展性 | 性能開銷大(JVM 無法優化反射調用) |
| 配合動態編譯/代理實現框架級功能(如 Spring、Hibernate) | 破壞封裝性,可訪問私有成員,降低安全性 |
結論: 反射是雙刃劍,框架開發必備,業務代碼慎用。
package com.bjpowernode.util;
import java.lang.reflect.Field;
public class ReflectUtil {
// 三種加載方式
public static void load(String classPath) throws ClassNotFoundException {
Class.forName(classPath);
}
public static void load(Class classObj) { /* 已在內存中 */ }
public static void load(Object obj) { obj.getClass(); }
// 獲取 public 屬性(含繼承)
public static void getInfo(Object obj) {
Field[] fields = obj.getClass().getFields();
for (Field f : fields) System.out.println(f.getName());
}
// 獲取本類聲明的所有屬性
public static void getInfo2(Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field f : fields)
System.out.println("屬性名" + f.getName() + " 數據類型" + f.getType().getName());
}
}
// Person.java
public class Person { public String name; }
// StudentVO.java
public class StudentVO extends Person {
public int sid;
protected int age;
private int score;
}
// 獲取 public 屬性(含繼承的 name)
public class 獲得陌生對象中共有屬性名稱 {
public static void main(String[] args) {
Object stu = new StudentVO();
ReflectUtil.getInfo(stu); // 輸出: name, sid
}
}
// 獲取本類聲明的所有屬性
public class 獲得陌生對象定義的所有屬性 {
public static void main(String[] args) {
Object obj = new StudentVO();
ReflectUtil.getInfo2(obj); // 輸出: sid, age, score
}
}
反射賦予 Java 動態檢測與操作類結構的能力,是現代框架的基石。掌握 Class、Field、Method、Constructor 四大核心類,理解加載方式與訪問邊界,方能在框架擴展、動態代理、注解處理等場景游刃有余。