📌 匿名内部类是什么?
没有名字的内部类,在创建对象的同时定义类体。
🎯 一句话理解
直接 new 接口或类,并立即实现/重写方法。
💡 基本语法
1. 实现接口
java
// 接口
interface Animal {
void makeSound();
}
public class Demo {
public static void main(String[] args) {
// 匿名内部类:直接new接口
Animal dog = new Animal() {
@Override
public void makeSound() {
System.out.println("汪汪!");
}
};
dog.makeSound(); // 输出:汪汪!
}
}
2. 继承抽象类
java
// 抽象类
abstract class Person {
abstract void speak();
void breathe() {
System.out.println("呼吸中...");
}
}
public class Demo {
public static void main(String[] args) {
// 匿名内部类:继承抽象类
Person student = new Person() {
@Override
void speak() {
System.out.println("我是学生");
}
// 可以重写父类方法
@Override
void breathe() {
super.breathe();
System.out.println("学生呼吸");
}
};
student.speak(); // 输出:我是学生
student.breathe(); // 输出:呼吸中... 学生呼吸
}
}
3. 继承普通类
java
// 普通类
class Printer {
void print(String msg) {
System.out.println("打印: " + msg);
}
}
public class Demo {
public static void main(String[] args) {
// 匿名内部类:继承普通类
Printer colorPrinter = new Printer() {
@Override
void print(String msg) {
System.out.println("彩色打印: " + msg);
}
// 可以添加新方法(但外部不能调用)
void scan() {
System.out.println("扫描功能");
}
};
colorPrinter.print("文档"); // 输出:彩色打印: 文档
// colorPrinter.scan(); // ❌ 编译错误
}
}
🚀 实际应用场景
场景1:事件监听(最常用)
java
// 点击监听接口
interface OnClickListener {
void onClick();
}
// 按钮类
class Button {
private OnClickListener listener;
void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
void click() {
if (listener != null) {
listener.onClick();
}
}
}
public class EventDemo {
public static void main(String[] args) {
Button button = new Button();
// 使用匿名内部类设置点击事件
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick() {
System.out.println("按钮被点击了!");
System.out.println("执行操作...");
}
});
button.click(); // 输出:按钮被点击了!执行操作...
}
}
场景2:线程创建
java
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程(传统方式)
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1运行中");
}
});
// Java 8+:Lambda表达式(更简洁)
Thread thread2 = new Thread(() -> {
System.out.println("线程2运行中");
});
thread1.start();
thread2.start();
}
}
场景3:集合排序
java
import java.util.*;
public class SortDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList("张三", "李四", "王五");
// 使用匿名内部类进行自定义排序
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
// 按字符串长度排序
return Integer.compare(a.length(), b.length());
}
});
System.out.println("排序后: " + names);
// Java 8+:Lambda表达式
Collections.sort(names, (a, b) -> b.length() - a.length());
System.out.println("反向排序: " + names);
}
}
🔑 核心特点
1. 没有类名
java
interface Greeting {
void sayHello();
}
public class Demo {
public static void main(String[] args) {
// 这里没有类名,直接new接口
Greeting greeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("你好!");
}
// 这个类没有名字,无法在其他地方使用
};
}
}
2. 只能使用一次
java
interface Calculator {
int calculate(int a, int b);
}
public class Demo {
public static void main(String[] args) {
// 创建加法计算器(只能用这一次)
Calculator adder = new Calculator() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
System.out.println("5 + 3 = " + adder.calculate(5, 3));
// 如果需要乘法,必须再创建一个
Calculator multiplier = new Calculator() {
@Override
public int calculate(int a, int b) {
return a * b;
}
};
System.out.println("5 * 3 = " + multiplier.calculate(5, 3));
}
}
3. 访问外部变量(必须是final)
java
public class VariableDemo {
public static void main(String[] args) {
final String message = "Hello"; // 必须是final
Runnable task = new Runnable() {
@Override
public void run() {
// 可以访问final局部变量
System.out.println(message);
// 不能修改(因为实际上是final)
// message = "Hi"; // ❌ 编译错误
}
};
new Thread(task).start();
}
}
⚡ 匿名内部类 vs Lambda表达式
对比示例
java
interface MathOperation {
int operate(int a, int b);
}
public class CompareDemo {
public static void main(String[] args) {
// 匿名内部类写法
MathOperation add1 = new MathOperation() {
@Override
public int operate(int a, int b) {
return a + b;
}
};
// Lambda表达式写法(Java 8+)
MathOperation add2 = (a, b) -> a + b;
System.out.println("匿名内部类: " + add1.operate(5, 3));
System.out.println("Lambda: " + add2.operate(5, 3));
}
}
使用选择
markdown
问:用匿名内部类还是Lambda?
1. 接口只有一个抽象方法?
- 是 → 用Lambda表达式 ✅(更简洁)
- 否 → 用匿名内部类
2. 需要重写多个方法?
- 是 → 用匿名内部类
- 否 → 用Lambda
3. 需要访问外部类的非final变量?
- 是 → 用匿名内部类(Lambda要求final)
- 否 → 用Lambda
总结:
- 简单情况:用Lambda
- 复杂情况:用匿名内部类
💎 实用技巧
1. 方法参数直接使用
java
interface Validator {
boolean validate(String input);
}
class Form {
void submit(Validator validator) {
// 使用传入的验证器
if (validator.validate("test")) {
System.out.println("验证通过");
}
}
}
public class ParamDemo {
public static void main(String[] args) {
Form form = new Form();
// 直接在参数中使用匿名内部类
form.submit(new Validator() {
@Override
public boolean validate(String input) {
return input != null && input.length() > 0;
}
});
}
}
2. 一次性任务
java
public class OneTimeTask {
public static void main(String[] args) {
// 一次性任务:文件处理
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("开始处理文件...");
// 处理逻辑
System.out.println("文件处理完成");
}
}).start();
}
}
⚠️ 注意事项
- 没有构造器:不能定义构造方法
- 不能有静态成员:不能有static方法和变量
- 只能继承一个类或实现一个接口
- 代码可读性:复杂的匿名内部类影响可读性
📝 记忆口诀
markdown
匿名内部类三要素:
1. new 接口/类
2. 立即实现/重写方法
3. 没有类名,只能用一次
使用场景:
1. 事件监听
2. 线程创建
3. 临时实现
4. 参数传递
🎯 一句话总结
匿名内部类就是"即用即丢"的临时实现,适合简单的一次性任务。复杂情况还是用正常类。