1. 引言

在本篇文章中,我们将深入探讨 Java 中的匿名类(Anonymous Classes)。
主要内容包括:

  • 匿名类的声明与实例化
  • 匿名类的特性与限制
  • 实际使用场景与最佳实践

通过本文,你可以掌握如何在实际开发中高效使用匿名类,并避免一些常见“坑”。


2. 匿名类的声明

匿名类是没有名字的内部类
正因为没有类名,我们无法像普通类那样通过 new ClassName() 的方式创建其实例。
所以,匿名类的声明与实例化必须在同一表达式中完成

我们可以基于:

  • 已有的类(通过继承)
  • 或者一个接口(通过实现)

来创建匿名类。

2.1 继承一个类

语法如下:

new SuperClassConstructor(params) {
    // override methods
};

示例:

new Book("Design Patterns") {
    @Override
    public String description() {
        return "Famous GoF book.";
    }
}

📌 注意:

  • 括号中传入的是父类构造函数所需的参数
  • 如果父类构造函数无参,则括号留空即可

2.2 实现一个接口

接口没有构造函数,因此括号始终为空:

new InterfaceName() {
    // implement interface methods
};

示例:

new Runnable() {
    @Override
    public void run() {
        System.out.println("Running...");
    }
}

你也可以将这个匿名类的实例赋值给一个变量,便于后续使用:

Runnable action = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running...");
    }
};

或者直接内联使用(比如添加到集合中):

List<Runnable> actions = new ArrayList<>();
actions.add(new Runnable() {
    @Override
    public void run() {
        System.out.println("In list...");
    }
});

⚠️ 但要注意:如果匿名类体太长,会严重影响代码可读性,建议控制其规模。


3. 匿名类的特性

匿名类与普通类在使用上有一些显著差异,以下是几个关键点。

3.1 构造函数

  • 匿名类只能实现一个接口或继承一个类,不能多继承
  • 匿名类不能是抽象类(abstract)
  • 匿名类不能有显式的构造函数定义
  • 实例化时只能创建一个对象

不过这并不影响我们使用:

  • 匿名类在声明时就完成实例化
  • 可以访问外部类的成员和局部变量(前提是这些变量是 final 或 effectively final)

3.2 静态成员

✅ 匿名类中可以有 static final 常量,但不能有普通静态成员变量:

new Runnable() {
    static final int x = 0;   // ✅ OK
    static int y = 0;         // ❌ 编译错误
    @Override
    public void run() {}
};

编译器会报错:

The field y cannot be declared static in a non-static inner type, unless initialized with a constant expression

3.3 变量作用域

匿名类可以访问其所在作用域中的局部变量,但这些变量必须是:

  • 显式声明为 final
  • 或者是 effectively final(JDK 8+)

示例:

int count = 1;
Runnable action = new Runnable() {
    @Override
    public void run() {
        System.out.println("Count: " + count);
    }
};

如果变量不是 effectively final,比如:

int count = 1;
count = 2;
Runnable action = new Runnable() {
    @Override
    public void run() {
        System.out.println(count); // ❌ 编译错误
    }
};

会提示:

local variables referenced from an inner class must be final or effectively final

✅ 匿名类还可以访问外部类的所有成员(包括 private)。


4. 使用场景

匿名类在实际开发中有不少用武之地,下面列举几个常见场景。

4.1 类结构与封装

当你想实现一个非常具体、仅在某处使用的类时,使用匿名类可以:

  • 避免创建不必要的顶层类文件
  • 更好地封装数据和行为
  • 避免暴露不必要的成员给外部类

4.2 简化项目结构

如果你只是想临时修改某个类的方法实现,而不希望创建一个新的 .java 文件,匿名类是个好选择。

尤其适用于:

  • 临时实现某个接口
  • 临时继承某个类并重写部分方法

4.3 UI 事件监听器

这是匿名类最经典的使用场景之一。

比如在 Java Swing 中:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked!");
    }
});

📌 自从 Java 8 引入 Lambda 表达式后,这种场景更推荐使用 Lambda,代码更简洁:

button.addActionListener(e -> System.out.println("Button clicked!"));

5. 嵌套类总览

匿名类其实是嵌套类(Nested Classes)的一种。

Java 中的嵌套类主要包括:

类型 特点
静态成员类(Static Member Class) 属于外部类,不能访问外部类的非静态成员
非静态成员类(Non-static Member Class) 即“内部类”,可以访问外部类所有成员
局部类(Local Class) 定义在方法或代码块中
匿名类(Anonymous Class) 没有名字的局部类,用于一次性使用

nested classes


6. 小结

✅ 本文我们系统地讲解了 Java 中匿名类的使用方式和注意事项,包括:

  • 匿名类的声明与实例化方式
  • 匿名类的构造函数、静态成员、变量作用域限制
  • 实际使用场景(如事件监听、UI 编程)
  • 与嵌套类体系的关系

📌 总结一下:

  • 匿名类适合一次性、短小精悍的实现
  • 使用时注意变量访问限制
  • 从 Java 8 开始,Lambda 表达式可以替代很多匿名类场景,更简洁清晰

如需查看完整代码示例,欢迎访问 GitHub 仓库


原始标题:Anonymous Classes in Java | Baeldung