cleanable 是 cleaner 的静态内部类,代表一次已注册的清理任务,不可继承或实现;它通过 cleaner.register() 获取,支持自动(gc 后)和手动(clean())清理,替代不可靠的 finalize()。

Java 9 引入的 Cleaner 类不是接口,Cleanable 才是它的核心配套类型——它是一个**具体类(Cleaner.Cleanable)**,代表一次已注册的清理任务,用于替代过去不可靠的 finalize() 方法。它不叫“接口”,也不能被实现;它是 Cleaner 内部生成的、可手动触发清理的句柄。
Cleanable 是什么,为什么不能当接口用
Cleaner.Cleanable 是 Cleaner 的静态内部类,由 cleaner.register(...) 返回。它封装了目标对象与清理动作的绑定关系,本身不可继承或实现,只提供两个关键能力:
-
自动触发:当注册的对象变为不可达(即被 GC 标记为可回收)时,Cleaner 后台线程会自动执行其关联的
Runnable -
手动提前清理:调用
cleanable.clean()可立即执行清理逻辑,且仅执行一次(幂等),适合在显式关闭资源时使用
怎么正确拿到并使用 Cleanable
必须通过 Cleaner 实例注册获得,不能 new,也不应长期持有强引用以防阻碍目标对象回收:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 全局复用一个
static final Cleaner,避免重复创建线程 - 在资源持有类的构造中调用
cleaner.register(this, cleanupAction) - 把返回的
Cleanable存为成员变量,供后续close()或异常清理调用
示例:
private final Cleaner.Cleanable cleanable;private final FileChannel channel;
public ManagedFileChannel(FileChannel ch) {
this.channel = ch;
this.cleanable = cleaner.register(this, () -> {
try { ch.close(); } catch (IOException ignored) {}
});
}
和 finalize 的本质区别在哪
关键不在“有没有 Cleanable”,而在于机制设计完全不同:
-
finalize()是对象方法,绑定在类上,GC 必须暂停、排队、跨线程调用,还可能跳过或重入 -
Cleanable是弱绑定的清理句柄,基于虚引用 + 引用队列,清理动作在独立后台线程异步运行,完全不阻塞 GC - 对象是否被回收,只取决于强引用是否断开;
Cleanable不影响生命周期判断,只响应结果
常见误用提醒
- 不要在清理动作里持有注册对象的强引用(如
this::close在非静态上下文中可能隐含强引用) - 不要让
Cleanable成为静态字段或全局缓存——它只为单个对象服务 - 清理动作中禁止抛出未捕获异常,否则该 Cleaner 实例的后续任务会被静默禁用
- 若对象在注册前已不可达,
Cleanable可能立刻触发清理,但顺序无法保证
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










