Java 匿名类简介

匿名类(Anonymous Inner Class)是 Java 中一种特殊的内部类,它没有显式的类名 ,通常用于一次性实现某个接口或继承某个类,并在定义处立即创建实例。它是 Java 早期实现回调、事件监听等场景的常用手段,也是理解内部类机制的重要一环。

一、为什么叫"匿名"?

普通类有明确的类名,例如:

java 复制代码
class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("执行任务");
    }
}

使用时通过 new MyTask() 创建对象。而匿名类省略了 class MyTask 这一命名步骤,直接把类的实现体写在 new 后面:

java 复制代码
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("执行任务");
    }
};

这里没有给这个类起名字,所以称为"匿名类"。编译器在编译时会自动为它生成一个类似 外部类$1.class 的字节码文件,但源码中不可见。

二、基本语法

java 复制代码
new 父类型(参数) {
    // 类体:重写方法、定义字段等
};

其中"父类型"可以是:

  • 一个接口
  • 一个抽象类
  • 一个具体类

匿名类必须恰好选择其中一种,不能同时继承类和实现接口。它是一次性使用的,定义的同时会创建对象,末尾需要分号。

三、严格分类与示例

根据父类型的性质,匿名类可以严格分为以下三类,互不重叠。

3.1 实现接口的匿名类

父类型是接口,匿名类必须实现接口中的所有抽象方法。

java 复制代码
interface Greeting {
    void sayHello();
}

public class Main {
    public static void main(String[] args) {
        Greeting g = new Greeting() {
            @Override
            public void sayHello() {
                System.out.println("Hello from anonymous class!");
            }
        };
        g.sayHello();  // 输出:Hello from anonymous class!
    }
}

典型场景 :事件监听器、RunnableComparator 等函数式接口(Java 8 后常被 Lambda 替代)。

3.2 继承抽象类的匿名类

父类型是抽象类,匿名类必须实现所有抽象方法。抽象类中已有的具体方法可以直接继承使用。

java 复制代码
abstract class Shape {
    abstract double area();

    void print() {
        System.out.println("面积:" + area());
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape() {
            double r = 5.0;   // 可以添加自己的字段

            @Override
            double area() {
                return Math.PI * r * r;
            }
        };
        circle.print();  // 输出:面积:78.53981633974483
    }
}

典型场景:需要复用抽象类中的部分实现,同时补充抽象方法。

3.3 继承具体类的匿名类

父类型是一个普通的具体类,匿名类可以选择性地重写父类方法,也可以添加自己的成员。

根据父类构造器的调用方式,又可分为两种子情况。

3.3.1 调用无参构造器

父类有无参构造器时,直接写 new 父类() { ... }

java 复制代码
class Animal {
    void sound() {
        System.out.println("动物发出声音");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal cat = new Animal() {
            @Override
            void sound() {
                System.out.println("喵喵");
            }
        };
        cat.sound();  // 输出:喵喵
    }
}
3.3.2 调用有参构造器

父类只有带参构造器时,必须在 new 时传入相应参数。

java 复制代码
class Person {
    String name;

    Person(String name) {
        this.name = name;
    }

    void say() {
        System.out.println("我是 " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p = new Person("张三") {
            @Override
            void say() {
                System.out.println("匿名子类说:我是 " + name);
            }
        };
        p.say();  // 输出:匿名子类说:我是 张三
    }
}

典型场景 :继承 Thread 类并重写 run(),同时调用 Thread(String name) 构造器。

java 复制代码
Thread t = new Thread("worker") {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 正在运行");
    }
};
t.start();  // 输出:worker 正在运行

四、通用特点与限制

无论哪种分类,匿名类都具有以下共同特点:

  • 可以访问外部类的成员,包括私有成员。
  • 可以访问方法中的局部变量 ,但该变量必须是 final 或"实际上的 final"(effectively final)。
  • 不能定义静态成员 (除非是 static final 常量)。
  • 不能有显式构造器,但可以通过实例初始化块完成初始化。
  • 隐式持有外部类引用,若生命周期过长可能导致内存泄漏。
  • 没有类名,因此无法在其他地方复用,也无法被继承。

五、与 Lambda 表达式的区别

Java 8 引入 Lambda 后,部分匿名类可以被更简洁的 Lambda 替代,但二者有本质区别:

特性 匿名类 Lambda
适用父类型 接口、抽象类、具体类 仅函数式接口(单抽象方法)
语法 较冗长 简洁
this 指向 匿名类自身 外部类
能否添加字段/方法 可以 不能
字节码 生成独立 .class 文件 通常使用 invokedynamic

因此,当接口包含多个抽象方法,或需要继承抽象类/具体类,或需要在实现中增加额外字段、方法时,匿名类仍是唯一选择。

六、总结

匿名类是一种"定义即使用"的临时类,适合逻辑简单、只使用一次的场景。根据父类型,它严格分为实现接口继承抽象类继承具体类三类,其中继承具体类又可按构造器调用方式细分。匿名类让代码更紧凑,但也可能降低可读性。在现代 Java 开发中,函数式接口优先使用 Lambda;而对于多方法接口、抽象类或具体类的扩展,匿名类依然不可或缺。

相关推荐
用户3126874877201 小时前
Java 泛型擦除到底擦掉了什么?类型安全与桥接方法深度拆解
java
暖焰核心1 小时前
继承全解——继承、默认成员函数、切片、隐藏与虚继承
java·前端·javascript
君顾11 小时前
外卖CPS系统开发实战指南:从架构设计到部署全流程解析
java·开发语言·外卖
奇牙coding1232 小时前
GPT-5.5 升级 GPT-5.6 接入指南:流式调用配置与常见问题
java·网络·gpt·ai
老刘的望远镜2 小时前
AgentScope Java 从零(03):Agent 的记性默认全开,我在第 50 轮翻了车
java·开发语言·javascript
yxlalm2 小时前
5.3解决超卖问题
java·spring boot·超卖
李少兄3 小时前
Java 中只有值传递吗?
java·开发语言·python
名字还没想好☜3 小时前
Spring @EventListener 事件驱动解耦实战:同步转异步、事务绑定与顺序控制
java·数据库·后端·python·spring
一木 之林3 小时前
插件、MCP、Skill 的区别?
java·c++·人工智能