抽象类不直接管理淘汰算法,而是为lru、lfu等策略提供统一接口和骨架逻辑:封装缓存容器、容量控制及增删查流程,将“该删谁”“如何更新热度”等策略相关决策点留给子类实现。

抽象类本身不直接“管理”淘汰算法,而是为不同淘汰策略(如 LRU、LFU、FIFO)提供统一接口和可复用的骨架逻辑。核心思路是:把缓存容器、容量控制、增删查基本流程抽到抽象类中,把**与具体淘汰策略强相关的决策点**(比如“该删谁?”、“访问后如何更新热度?”)留给子类实现。
定义缓存通用行为契约
抽象类声明必须由子类实现的关键方法,例如:
- onAccess(K key):每次 get 或 put 命中时调用,子类在此决定如何更新该 key 的“热度状态”(如 LRU 移到链表尾,LFU 增加计数)
- selectEvictKey():缓存满时调用,子类返回应被淘汰的 key(如 LRU 返回链表头 key,LFU 返回最小计数且最老的 key)
- onEvict(K key, V value):实际删除前回调,可用于清理资源或日志(可选钩子)
封装不变的基础设施与流程
抽象类内部维护:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 一个 Map
存真实数据(如 HashMap 或 ConcurrentHashMap) - 一个 int capacity 和 int size 控制上限
- 统一的 get(K) 实现:查 map → 若存在则调 onAccess() → 返回值;否则返回 null
- 统一的 put(K, V) 实现:若已存在则更新 + onAccess();若不存在且 size ≥ capacity,则先 selectEvictKey() → onEvict() → 删除 map 中对应项 → size--;最后插入新项 + size++
让子类专注策略差异,不重复造轮子
以 LRU 为例,子类只需:
- 持有一个 LinkedList
或自定义双向链表,记录访问顺序 - 重写 onAccess(K):从链表中移除 key 并 addLast(key)
- 重写 selectEvictKey():return linkedList.getFirst()
- 无需再写 get/put 的边界判断、容量检查、map 同步等重复逻辑
避免常见设计陷阱
注意三点:
- 抽象类中 不要暴露底层数据结构细节(如不暴露 LinkedList 引用),防止子类绕过骨架直接操作,破坏一致性
- onAccess 和 selectEvictKey 必须在同一个锁或同步块内执行,尤其在并发场景下,避免 get 和 put 交错导致状态错乱
- 如果支持多种策略共存(如运行时切换),可在抽象类中加 protected 字段标记当前策略类型,但淘汰逻辑仍由子类实例承担,不建议在抽象类里 if-else 分支实现所有策略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










