75. Java 嵌套类 - 匿名类
在 Java 开发中,我们经常会写一些 一次性 使用的类,比如:
- 实现一个接口
- 扩展一个类
- 只需要在某个方法里使用一次
如果用普通类来写,这样的代码会很冗长。所以,Java 提供了一种 更简洁的方式 —— 匿名类(Anonymous Class)。
什么是匿名类(Anonymous Classes)?
匿名类和 局部类(Local Class) 很像,但它没有名字! 换句话说,它只在声明它的地方创建,不需要单独的类名。
匿名类的特点
✅ 只使用一次:适用于临时对象,不需要重复使用的类。 ✅ 没有类名:在声明的同时 进行 实例化。 ✅ 可以实现接口,也可以继承类。 ✅ 不能有构造方法(但可以有实例初始化块)。
匿名类 vs. 局部类
| 特性 | 局部类(Local Class) | 匿名类(Anonymous Class) |
|---|---|---|
| 是否有名字 | ✅ 有 | ❌ 没有 |
| 作用范围 | 只能在定义它的方法/代码块内使用 | 只能在创建它的那一行代码中使用 |
| 代码量 | 相对较多(需要先定义类,再创建对象) | 更简洁(直接在实例化时定义) |
| 适用场景 | 可能会多次使用的辅助类 | 只用一次的小型逻辑实现 |
示例 1:用匿名类实现接口
来看一个例子:我们要实现一个 HelloWorld 接口,并创建不同语言的问候。
代码
public class HelloWorldAnonymousClasses {
interface HelloWorld {
void greet();
void greetSomeone(String someone);
}
public void sayHello() {
// 普通局部类实现
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting(); // 使用局部类
// 匿名类实现(French)
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
// 匿名类实现(Spanish)
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
运行结果
Hello world
Salut Fred
Hola, mundo
代码解析
1️⃣ 局部类 EnglishGreeting:
- 这个类有一个
name变量,默认值是"world"。 greet()方法会调用greetSomeone("world")。greetSomeone(String someone)方法可以修改name,然后打印问候语。
2️⃣ 匿名类 frenchGreeting 和 spanishGreeting:
- 没有类名,直接在 创建对象时 定义类的实现。
- 实现了
HelloWorld接口,但写法更加简洁。 - 直接在实例化的时候,实现接口里的方法。
匿名类的语法
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
匿名类表达式的组成
| 语法部分 | 作用 |
|---|---|
new | 创建新对象 |
HelloWorld() | 实现的接口(或继承抽象类) |
{} | 匿名类的类体(这里定义了变量和方法) |
关键点:
- 接口不能有构造方法,所以这里的
()是空的。 - 匿名类的类体 里可以有 实例变量 和 方法。
匿名类 vs. 继承类
匿名类不仅可以 实现接口,还能 继承类。
示例 2:匿名类继承抽象类
abstract class Animal {
abstract void makeSound();
}
public class AnonymousClassExample {
public static void main(String[] args) {
Animal dog = new Animal() {
public void makeSound() {
System.out.println("Woof Woof!");
}
};
dog.makeSound(); // 输出:Woof Woof!
}
}
这里的 dog 对象:
- 继承了
Animal类,但没有给它起名字。 - 直接在创建
dog对象时,重写了makeSound()方法。
匿名类的局限
尽管匿名类很方便,但它也有一些限制: ❌ 不能有构造方法(但可以有实例初始化块)。
❌ 不能有 static 方法或变量(除了 static final 常量)。
❌ 只能使用一次,如果需要多次使用,应该使用 普通类 或 局部类。
示例 3:匿名类不能有 static 方法
public class Test {
public static void main(String[] args) {
Animal dog = new Animal() {
static void staticMethod() { // ❌ 错误:匿名类不能有 static 方法
System.out.println("This won't compile!");
}
public void makeSound() {
System.out.println("Woof Woof!");
}
};
}
}
编译错误:
修饰符 static 仅在常量变量声明中允许
适用场景
✅ 适合用匿名类的情况
- 只需要用 一次 的类,例如 事件监听器、回调函数、临时实现。
- 需要 快速实现接口,但不想写单独的类文件。
❌ 不适合用匿名类的情况
- 代码逻辑比较复杂,需要 多次使用(建议改成 普通类)。
- 需要 构造方法或静态方法(匿名类不支持)。
- 需要定义 多个方法(局部类更适合)。
总结
| 特点 | 匿名类 |
|---|---|
| 作用 | 一次性使用 |
| 是否有名字 | ❌ 没有 |
| 是否能有构造方法 | ❌ 不能(但可以有实例初始化块) |
是否能有 static 方法 | ❌ 不能 |
| 适用于 | 简短的接口实现或类扩展 |
希望这次讲解能让你更清楚 匿名类的用法和局限!🚀 🎯