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<?> 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
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(); // 僅適用無參構造器
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
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());
}
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<?> intArrayClass = Class.forName("[I"); // int[]
Class<?> compType = intArrayClass.getComponentType(); // int
// 引用類型數組
Class<?> strArrayClass = Class.forName("[Ljava.lang.String;"); // String[]
反射是 Java 動態性的基石,框架開發(Spring、Hibernate、MyBatis 等)大量依賴反射。掌握反射能讓你:
注意:反射有性能開銷(JIT 優化較難)、破壞封裝性、編譯期無法檢查錯誤,業務代碼中應謹慎使用。