匿名內部類是 Java 中實現「即時實現接口/繼承類並創建對象」的核心語法糖,廣泛應用於事件監聽、多線程、回調模式等場景。本文將從語法本質、編譯原理、Android 典型應用三個維度,系統梳理匿名內部類的完整知識體系。
// 通用形式
new 父類或接口() {
// 類體:可訪問外部類成員、重寫方法、定義新字段/方法
};
// 實現接口示例
interface Printer { void print(); }
Printer p = new Printer() {
@Override public void print() { System.out.println("匿名實現"); }
};
編譯器會生成形如 OuterClass$1.class、OuterClass$2.class 的獨立類文件,關鍵特徵:
OuterClass.this)及被捕獲的 final/效能性 final 變量。OuterClass.this,可能導致外部類無法 GC(內存洩漏隱患)。{ ... })執行初始化邏輯。
Button btn = findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 處理點擊邏輯
Toast.makeText(this, "Clicked", Toast.LENGTH_SHORT).show();
}
});
解析:new View.OnClickListener() { ... } 創建實現接口的匿名類實例,{} 是類體而非代碼塊。這是「策略模式」的輕量級應用。
// 方式一:字段初始化
private Runnable mRunnable = new Runnable() {
@Override public void run() {
try {
Thread.sleep(5000);
mHandler.sendEmptyMessage(0);
} catch (InterruptedException e) { e.printStackTrace(); }
}
};
// 方式二:局部變量傳遞
new Thread(new Runnable() {
@Override public void run() { /* 耗時任務 */ }
}).start();
public Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
refreshUI(); // 主線程更新 UI
}
};
關鍵點:匿名繼承 Handler 並重寫 handleMessage,構造時傳入主線程 Looper,實現跨線程消息分發。
void demo(final String param) { // JDK 8+ 可省略 final
String local = "local";
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(param); // ✅ 捕獲參數
System.out.println(local); // ✅ 捕獲效能性 final 變量
// local = "changed"; // ❌ 編譯錯誤:捕獲變量不可修改
}
};
}
final。| 陷阱 | 風險 | 解決方案 |
|---|---|---|
| 非 static 匿名類持有外部類引用 | 內存洩漏(如 Handler 延遲消息導致 Activity 洩漏) | 改用 static 內部類 + WeakReference;或 Kotlin 協程替代 |
| 捕獲循環變量導致邏輯錯誤 | 所有實例共享同一最終值 | 在循環內定義 final 局部變量複製值 |
| 匿名類過大影響可讀性 | 代碼維護困難 | 提取為具名內部類或獨立類;Java 8+ 改用 Lambda |
| 嘗試調用匿名類特有方法 | 編譯錯誤:引用類型為父類/接口 | 提取為具名類,或使用反射(不推薦) |
Java 8 起,函數式接口(單抽象方法)可用 Lambda 替代:
// 匿名內部類
btn.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) { doSomething(); }
});
// Lambda(更簡潔,無 this 持有問題)
btn.setOnClickListener(v -> doSomething());
// 方法引用
btn.setOnClickListener(this::doSomething);
注意:Lambda 不創建額外類文件,無 this 指向問題,性能更優;但需接口為函數式接口(@FunctionalInterface)。
匿名內部類是 Java 「接口導向編程」與「回調模式」的基石語法。掌握其編譯生成機制、外部引用持有風險、變量捕獲規則,並結合現代 Lambda/方法引用 靈活選型,是寫出高質量 Java/Android 代碼的關鍵技能。