類加載器與反射是 Java 動態性的核心支柱,支撐著 Spring、MyBatis 等主流框架的 IOC、AOP、插件機制。本文系統講解類加載全生命週期、反射核心 API 實戰、動態代理原理及經典設計模式應用。
java.lang.Class 對象,作為方法區數據的訪問入口。<clinit>() 方法:靜態變量賦值、靜態代碼塊、按源碼順序執行。new、反射、克隆、反序列化創建實例。Class.forName()、ClassLoader.loadClass()。main() 入口)。Bootstrap ClassLoader (C++ 實現)
↑ 父加載器
Extension ClassLoader (sun.misc.Launcher$ExtClassLoader)
↑ 父加載器
System/App ClassLoader (sun.misc.Launcher$AppClassLoader) ← 默認應用加載器
↑ 父加載器
Custom ClassLoader (用戶自定義)
| 加載器 | 加載路徑 | 典型類 |
|---|---|---|
| Bootstrap | JAVA_HOME/jre/lib/rt.jar 等核心庫 | java.lang.*, java.util.* |
| Extension | JAVA_HOME/jre/lib/ext/*.jar 或 java.ext.dirs | JAXP, 加密API 等擴展 |
| System | classpath / -cp 指定路徑 | 應用主類、第三方庫 |
雙親委派機制:收到加載請求 → 委派給父加載器 → 遞歸至 Bootstrap → 父加載器無法加載時子加載器嘗試。
優勢:避免重複加載、保護核心類不被篡改(沙箱安全)。
打破委派:Tomcat/WebLogic 為實現熱部署、模塊隔離,自定義加載器優先加載 Webapp 類庫。
核心入口:獲取 Class 對象三種方式:
// 1. Object.getClass() — 已有實例
Class> c1 = "hello".getClass();
// 2. 類字面量 — 編譯期已知類
Class> c2 = String.class;
// 3. Class.forName() — 運行期動態決定(框架首選,支持配置文件解耦)
Class> c3 = Class.forName("com.example.User");
Class> clazz = Class.forName("com.example.User");
// 獲取 public 構造器
Constructor> conPub = clazz.getConstructor(String.class, int.class);
User u1 = (User) conPub.newInstance("Alice", 25);
// 獲取私有構造器(暴力反射)
Constructor> conPri = clazz.getDeclaredConstructor(String.class);
conPri.setAccessible(true); // 關鍵:取消訪問檢查
User u2 = (User) conPri.newInstance("Bob");
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(user, "Charlie"); // 等同 user.name = "Charlie"
Object val = nameField.get(user); // 讀取
// 無參無返回
Method show = clazz.getMethod("show");
show.invoke(user);
// 帶參帶返回
Method getInfo = clazz.getMethod("getInfo", String.class, int.class);
String result = (String) getInfo.invoke(user, "param", 100);
// 私有方法
Method secret = clazz.getDeclaredMethod("secret");
secret.setAccessible(true);
secret.invoke(user);
ArrayList<Integer> 添加 String(利用運行期無泛型信息)。setProperty(obj, "fieldName", value) 實現對任意對象屬性賦值(框架封裝基石)。className/methodName,反射動態調用(插件化架構雛形)。靜態代理:代碼階段確定代理類。
動態代理:運行時通過反射生成代理類字節碼。
// 1. 定義接口
public interface UserDao { void save(); void find(); }
// 2. 目標實現
public class UserDaoImpl implements UserDao { ... }
// 3. InvocationHandler 實現攔截邏輯
public class LogHandler implements InvocationHandler {
private Object target;
public LogHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【日誌】" + method.getName() + " 開始");
Object result = method.invoke(target, args); // 調用目標方法
System.out.println("【日誌】" + method.getName() + " 結束");
return result;
}
}
// 4. 生成代理對象
UserDao target = new UserDaoImpl();
UserDao proxy = (UserDao) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogHandler(target)
);
proxy.save(); // 觸發攔截邏輯
限制:只能代理實現接口的類。
突破:CGLIB(繼承目標類)、ByteBuddy(字節碼操作庫),Spring AOP 自動選擇(有接口用 JDK,無接口用 CGLIB)。
getTime() 模板,code() 由子類實現(for 循環 / IO 複製)。super.call() 形成鏈路。new BufferedReader(new InputStreamReader(System.in))。// 裝飾器結構
interface Phone { void call(); }
class IPhone implements Phone { public void call() { System.out.println("打電話"); } }
abstract class PhoneDecorator implements Phone {
protected Phone phone;
public PhoneDecorator(Phone phone) { this.phone = phone; }
public void call() { phone.call(); }
}
class RingDecorator extends PhoneDecorator {
public RingDecorator(Phone p) { super(p); }
public void call() { System.out.println("播放彩鈴"); super.call(); }
}
class MusicDecorator extends PhoneDecorator {
public MusicDecorator(Phone p) { super(p); }
public void call() { super.call(); System.out.println("播放音樂"); }
}
// 使用
Phone p = new IPhone();
p = new RingDecorator(new MusicDecorator(p));
p.call(); // 彩鈴 → 打電話 → 音樂
ClassNotFoundException/NoClassDefFoundError、實現熱加載。