Java 反射允許執行期動態載入類別、建立實例、呼叫方法、存取欄位,甚至突破 private 封裝。本文以完整可執行範例碼,系統講解四大核心場景:無參構造實例化、有參構造實例化、方法呼叫(含私有)、欄位操作(含私有),並總結反射建立物件的標準流程與注意事項。
package test;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
private void test() { System.out.println("private method"); }
}
public static void createObj1() throws Exception {
// 方式一:Class.forName 載入類別(支援動態類別名)
Class> clazz = Class.forName("test.Person");
// 方式二:類別字面常數(編譯期已知類別)
// Class> clazz = Person.class;
Object obj = clazz.newInstance(); // 呼叫無參構造子
System.out.println("是否為 Person 實例:" + (obj instanceof Person));
}
public static void createObj2() throws Exception {
Class> clazz = Person.class;
// 取得對應參數型別的 Constructor 物件
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
// 透過 Constructor 建立實例
Person person = constructor.newInstance("測試", 23);
System.out.println("建立物件 name:" + person.getName());
}
public static void methodDo() throws Exception {
Person p = new Person();
Class> clazz = Person.class;
// 呼叫 public 方法
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(p, "ckl");
System.out.println("呼叫 setName 後:" + p.getName());
// 呼叫 private 方法:必須用 getDeclaredMethod + setAccessible
Method privateMethod = clazz.getDeclaredMethod("test");
privateMethod.setAccessible(true); // 關鍵:暴力反射突破封裝
privateMethod.invoke(p);
}
public static void fieldDo() throws Exception {
Person p = new Person();
Class> clazz = Person.class;
Field nameField = clazz.getDeclaredField("name");
Field ageField = clazz.getDeclaredField("age");
nameField.setAccessible(true);
ageField.setAccessible(true);
nameField.set(p, "反射測試");
ageField.set(p, 23);
System.out.println("name:" + p.getName() + ",age:" + p.getAge());
}
Class.forName("完整類別名")、類別名.class、實例.getClass()、基本型別 int.class/Integer.TYPE。clazz.newInstance()(已棄用,建議改用 clazz.getDeclaredConstructor().newInstance())。clazz.getConstructor(參數型別...).newInstance(實參...)。(目標類別) 物件 以獲得編譯期型別安全。
// 1. 類別字面常數(最常用、編譯期檢查)
Class> c1 = String.class;
// 2. Class.forName(動態載入、支援變數)
Class> c2 = Class.forName("java.lang.String");
// 3. 實例 getClass()
String s = "hello";
Class> c3 = s.getClass();
// 4. 基本型別與包裝類關係
Class> c4 = int.class; // 基本型別
Class> c5 = Integer.TYPE; // 等同 int.class
Class> c6 = Integer.class; // 包裝類
Method/Field/Constructor 物件。setAccessible(true) 受 SecurityManager 限制,模組化系統(JPMS)需在 module-info.java 開放 opens。TypeToken / ParameterizedType 處理。--add-opens 啟動參數。