覆盖单例、IO、JDK 新特性、Redis、Spring、内存模型、单例、反射等
1. 饿汉单例 vs 懒汉单例
表格
| 对比维度 | 饿汉单例 | 懒汉单例 |
|---|---|---|
| 实例创建时机 | 类加载时直接初始化 | 第一次调用 getInstance 才创建 |
| 线程安全 | 天然线程安全,无并发问题 | 不加锁多线程会创建多个实例 |
| 内存占用 | 程序启动即占用内存,常驻堆 | 不用则不占用内存,懒加载节约资源 |
| 依赖构造 | 无法传入动态参数 | 可支持运行时传参构造实例 |
| 适用场景 | 实例占用小、全局一定会使用 | 实例大、不一定每次程序都用到 |
2. 双重检查锁单例 (DCL) vs 静态内部类单例
表格
| 对比维度 | DCL 双重检查锁 | 静态内部类单例 |
|---|---|---|
| 实现复杂度 | 高,必须加 volatile 禁止指令重排 | 极简,无需锁、无需 volatile |
| 线程安全 | 正确写法才安全,极易写错 | 天然线程安全,JVM 类加载保证 |
| 延迟加载 | 支持延迟初始化 | 天然延迟加载 |
| 缺陷点 | 忘记 volatile 会出现半初始化对象 | 无法传递构造参数 |
| 推荐度 | 不推荐,坑多 | 生产首选无参单例 |
3. IO 字节流 InputStream vs 字符流 Reader
表格
| 对比维度 | 字节流 (InputStream/OutputStream) | 字符流 (Reader/Writer) |
|---|---|---|
| 操作单位 | byte 字节,二进制数据 | char 字符,文本数据 |
| 编码处理 | 无编码转换,原始二进制 | 内置编码解码,自动处理字符集 |
| 适用文件 | 图片、视频、压缩包、二进制文件 | txt、html、java 等文本文件 |
| 缓冲区 | 无自带字符缓冲区 | 自带字符缓冲区,读写文本效率更高 |
| 后缀区分 | 后缀 Stream | 后缀 Reader/Writer |
4. FileInputStream vs Files NIO.2(JDK7)
表格
| 对比维度 | 传统 FileInputStream | java.nio.file.Files |
|---|---|---|
| API 风格 | 老式 IO,代码繁琐,需手动关闭流 | NIO 工具类,一行读写,支持 try-with-resources |
| 功能能力 | 仅基础读写字节 | 支持复制、删除、遍历目录、读取行、权限操作 |
| 异常处理 | 底层 IOException,无封装 | 统一 IOException,提供批量工具方法 |
| 大文件性能 | 单字节读取慢,需手动缓冲 | 内置缓冲,支持流式逐行读取不加载全量 |
| JDK 版本 | JDK1.0 通用 | JDK7 新增 |
5. BIO 同步阻塞 IO vs NIO 非阻塞 IO
表格
| 对比维度 | BIO | NIO |
|---|---|---|
| 线程模型 | 一连接一线程,连接多线程爆炸 | 单线程 / 少量线程管理大量连接 |
| 阻塞特性 | 读写全程阻塞 | Selector 轮询,无连接阻塞等待 |
| 适用场景 | 短连接、并发量极低 | 高并发网络服务、网关、中间件 |
| 编程难度 | 简单易懂,入门友好 | API 复杂,需处理 Channel、Buffer、Selector |
| 资源消耗 | 高,大量线程占用栈内存 | 低,少量线程管理上万连接 |
6. JDK8 Stream vs JDK16 Stream.toList()
表格
| 对比维度 | Collectors.toList() | stream.toList() JDK16+ |
|---|---|---|
| 集合可变性 | 可变 ArrayList,支持 add/remove | 不可变 List,增删抛异常 |
| null 元素 | 允许存入 null 值 | 禁止 null,存入直接 NPE |
| 底层实现 | ArrayList | 内部不可变私有实现类 |
| 泛型推导 | 复杂泛型容易推导失败 | 泛型推断更稳定简洁 |
| 兼容版本 | JDK8 全版本可用 | 仅 JDK16 及以上 |
7. List.of() vs Collections.unmodifiableList()
表格
| 对比维度 | List.of() | Collections.unmodifiableList() |
|---|---|---|
| 创建时机 | 直接创建全新不可变集合 | 对原有集合做包装视图 |
| 原集合影响 | 独立集合,不受外部影响 | 原集合修改,视图同步变化 |
| null 支持 | 不允许存放 null 元素 | 允许包含 null |
| 扩容操作 | add/set/remove 直接抛异常 | 修改底层原集合才会报错 |
| JDK 版本 | JDK9+ | JDK1.2 老牌 API |
8. CompletableFuture supplyAsync vs runAsync
表格
| 对比维度 | supplyAsync | runAsync |
|---|---|---|
| 任务返回值 | Supplier,支持泛型返回结果 | Runnable,无返回值 Void |
| 异常获取 | get ()/join () 可捕获业务异常 | 异常同样捕获,但无业务数据 |
| 适用场景 | 异步查询、接口调用需要返回数据 | 纯执行任务,无需结果(日志、清理) |
| 函数入参 | 无参有返回 | 无参无返回 |
9. ThreadLocal vs 方法传参
表格
| 对比维度 | ThreadLocal | 方法参数传递 |
|---|---|---|
| 数据生命周期 | 绑定线程,线程池复用易泄漏 | 仅方法调用链路,执行完释放 |
| 代码侵入 | 全局隐式传递,方法无需加参数 | 所有调用链路都要增加参数,侵入大 |
| 线程安全 | 单线程隔离,多线程互不干扰 | 参数独立,天然无并发问题 |
| 内存风险 | 不调用 remove () 发生内存泄漏、数据串线 | 无内存泄漏风险 |
| 调试难度 | 隐式数据,链路难追踪 | 参数显式传递,调试清晰 |
10. 静态变量缓存 vs 本地缓存 Caffeine
表格
| 对比维度 | static Map 静态缓存 | Caffeine 高性能本地缓存 |
|---|---|---|
| 过期淘汰 | 无过期逻辑,永久驻留内存 | 支持基于时间、大小、引用自动淘汰 |
| 并发安全 | 需手动加锁,极易并发错乱 | 内置并发控制,线程安全 |
| 内存管控 | 无上限,数据无限膨胀 OOM | 可设置最大容量,自动清理冷数据 |
| 额外能力 | 仅简单存取 | 自动刷新、命中统计、弱引用缓存 |
| 适用规模 | 几十条少量固定配置 | 上万条高频业务本地缓存 |
11. 引用类型:强引用 vs 软引用 SoftReference
表格
| 对比维度 | 强引用 | 软引用 SoftReference |
|---|---|---|
| GC 回收规则 | 永远不回收,内存溢出才释放 | 内存不足时主动回收缓存对象 |
| 内存溢出风险 | 持有大对象极易 OOM | 内存紧张自动释放,缓解 OOM |
| 典型用途 | 业务正常对象、全局核心数据 | 图片缓存、临时查询缓存 |
| 重建成本 | 销毁后无法重建 | 可缓存可重建,内存不足自动释放 |
12. 弱引用 WeakReference vs 虚引用 PhantomReference
表格
| 对比维度 | 弱引用 WeakReference | 虚引用 PhantomReference |
|---|---|---|
| GC 回收时机 | 下次 Minor GC 直接回收 | 对象回收前入队列,做资源清理 |
| 获取对象 | get () 可短暂获取对象 | get () 永远返回 null,无法访问对象 |
| 业务场景 | 缓存、配合 WeakHashMap | 监控对象回收、释放堆外内存 |
| 生命周期 | GC 后直接清空引用 | 仅用于回收后置处理 |
13. 原生类型流 IntStream vs Stream<Integer>
表格
| 对比维度 | IntStream | Stream<Integer> |
|---|---|---|
| 装箱拆箱 | 无包装类,零装箱开销 | 频繁自动装箱拆箱,性能损耗 |
| 内置统计 | sum、max、min、average 原生方法 | 需要手动 collect 统计 |
| 收集限制 | 不能直接 Collectors,必须 boxed () | 可直接 toList/toMap |
| 内存占用 | 仅存 int 数值,占用极小 | 每个 Integer 带对象头,内存翻倍 |
14. Spring @Service vs @Component
表格
| 对比维度 | @Component | @Service |
|---|---|---|
| 关系 | 顶层通用组件注解 | @Component 派生注解 |
| 语义区分 | 通用任意组件,无业务含义 | 专门标记业务逻辑层 Service |
| 功能差异 | 底层扫描、实例化逻辑完全一致 | 仅语义区分,功能无差别 |
| 分层规范 | 工具类、公共组件 | 业务逻辑服务层 |
| 事务支持 | 无特殊标识 | 开发者约定 Service 层写事务 |
15. @Repository vs @Mapper(Mybatis)
表格
| 对比维度 | @Repository | @Mapper |
|---|---|---|
| 归属框架 | Spring 原生注解 | Mybatis 专属注解 |
| 异常转换 | 自动把数据库异常转为 Spring DataAccessException | 无异常转换能力 |
| 扫描规则 | Spring 组件扫描实例化 DAO | Mybatis 扫描生成代理 Mapper |
| 使用场景 | 原生 JDBC/Mybatis 通用 DAO | Mybatis XML / 注解 Mapper 接口专用 |
16. 接口参数校验 @Valid vs @Validated
表格
| 对比维度 | @Valid(JSR303) | @Validated (Spring 扩展) |
|---|---|---|
| 分组校验 | 不支持分组校验 | 支持 groups 分组校验,多场景复用 DTO |
| 嵌套校验 | 支持对象内部 @Valid 嵌套校验 | 不支持嵌套对象递归校验 |
| 来源规范 | Java 原生 JSR 规范 | Spring 框架扩展注解 |
| 适用场景 | 简单单一场景参数校验 | 多接口复用 DTO、多分组校验场景 |
17. 局部变量缓存对象 vs 重复调用 getter
表格
| 对比维度 | 提前缓存局部变量 | 循环内重复调用 getter |
|---|---|---|
| 执行开销 | 仅调用一次 getter,性能高 | 循环多次重复调用 getter,重复查询 |
| 线程一致性 | 缓存固定值,循环全程不变 | 每次获取最新对象,存在并发变化风险 |
| 代码可读性 | 少量临时变量,逻辑清晰 | 代码简短但隐藏重复方法调用 |
| 推荐场景 | getter 包含数据库 / 远程查询、复杂计算 | getter 仅简单 return 字段无计算 |
18. HashMap key 为基本包装类 vs 自定义实体
表格
| 对比维度 | Integer/Long/String 等内置类 | 自定义 POJO 实体作为 key |
|---|---|---|
| equals&hashCode | JDK 已重写,开箱即用 | 必须手动重写两个方法,否则去重失效 |
| 哈希散列分布 | 哈希算法均匀,碰撞少 | 重写不合理会大量哈希碰撞,性能暴跌 |
| 修改风险 | 不可变对象,修改无影响 | 修改对象字段后 hashCode 改变,无法 get/remove |
19. TreeSet vs HashSet
表格
| 对比维度 | TreeSet | HashSet |
|---|---|---|
| 底层结构 | TreeMap 红黑树 | HashMap 哈希表 |
| 有序性 | 元素自动升序排序 | 完全无序 |
| 读写效率 | O (logn),性能偏低 | O (1),读写极快 |
| 元素要求 | 实现 Comparable 或传入比较器 | 无排序要求,只需重写 equals+hashCode |
| 适用场景 | 需要有序不重复集合 | 普通去重、查询场景 |
20. 阻塞队列 ArrayBlockingQueue vs LinkedBlockingQueue
表格
| 对比维度 | ArrayBlockingQueue | LinkedBlockingQueue |
|---|---|---|
| 底层结构 | 固定长度数组,有界队列 | 链表,可无界 / 指定有界 |
| 锁机制 | 一把全局锁,读写互斥 | 两把独立锁,读、写分离并发更高 |
| 内存分配 | 初始化一次性分配数组内存 | 动态创建节点,内存按需分配 |
| 吞吐量 | 并发读写吞吐量低 | 高并发生产消费性能更好 |
| 容量限制 | 必须指定固定容量 | 不传参数则无界,无限添加易 OOM |
21. 方法参数传值(基本类型) vs 传引用(对象)
表格
| 对比维度 | 基本类型传值 | 引用对象传参 |
|---|---|---|
| 传递内容 | 拷贝变量原始数值 | 拷贝对象内存地址(引用) |
| 外部修改影响 | 方法内修改副本,不影响外部原值 | 修改对象内部属性,外部同步变化 |
| 修改引用地址 | 方法内重新赋值不影响外部 | 方法内重新 new 对象不影响外部原引用 |
22. try-catch 在外层 vs 循环内部捕获异常
表格
| 对比维度 | 循环内单独 try-catch | 循环外层整体 try-catch |
|---|---|---|
| 容错能力 | 单个元素失败不中断循环 | 一个元素异常直接终止整个循环 |
| 性能损耗 | 大量 try 块轻微损耗性能 | 仅一层异常捕获,性能更好 |
| 日志粒度 | 每个失败元素单独打印日志 | 只能捕获批量整体异常,无法定位单条 |
| 业务场景 | 批量处理单条失败允许跳过 | 批量任务任意失败全部回滚终止 |
23. 枚举 Enum vs 静态常量 static final
表格
| 对比维度 | 枚举类 Enum | 静态常量类 |
|---|---|---|
| 类型安全 | 强类型,只能传入指定枚举值 | int/String 可传入任意非法值,编译无报错 |
| 内置方法 | ordinal ()、name ()、values ()、switch 支持 | 无内置工具方法,需手动封装 |
| 扩展属性 | 可携带多个字段、构造方法、业务方法 | 仅单一常量值,无法携带多属性 |
| 序列化 | 天然序列化安全,版本兼容 | 自定义序列化易出现兼容问题 |
24. JDK 动态代理 vs CGLIB 动态代理
表格
| 对比维度 | JDK 动态代理 | CGLIB 代理 |
|---|---|---|
| 代理对象 | 仅实现接口的类 | 普通类、无接口类均可代理 |
| 实现原理 | 实现目标类所有接口 | 继承目标类生成子类 |
| 方法限制 | 只能拦截接口 public 方法 | 可拦截所有非 final 公共方法 |
| 依赖包 | JDK 原生无需第三方 | 早期 Spring 需引入 cglib 包,高版本内置 |
| 失效场景 | 目标类无接口 | 目标类有 final 修饰无法继承,代理失效 |
25. 本地变量 vs 成员实例变量
表格
| 对比维度 | 方法局部变量 | 实例成员变量 |
|---|---|---|
| 内存位置 | 栈帧,方法执行完立即销毁 | 堆中对象,随对象生命周期存在 |
| 默认初始值 | 无默认值,使用前必须赋值 | 有默认值(数字 0、引用 null) |
| 线程安全 | 每个线程独立栈,天然无并发问题 | 多线程共享实例,存在并发竞争 |
| 生命周期 | 短,方法执行结束释放 | 长,对象不回收则常驻堆 |
26. for-each 增强循环 vs Iterator 迭代器
表格
| 对比维度 | for-each 循环 | Iterator 手动迭代 |
|---|---|---|
| 底层依赖 | 语法糖,底层封装 Iterator | 原生迭代器 API,可控度高 |
| 删除元素 | 循环内调用集合 remove 直接抛异常 | iterator.remove () 安全删除元素 |
| 下标获取 | 无法获取当前遍历索引 | 无下标,需自行定义计数器 |
| 适用范围 | 仅实现 Iterable 接口集合 | Collection 全系列集合通用 |
27. BigDecimal double 构造 vs String 构造
表格
| 对比维度 | new BigDecimal(double) | new BigDecimal(String) |
|---|---|---|
| 精度丢失 | 二进制浮点精度丢失,存在误差 | 完全精准,无精度损失 |
| 金融场景 | 严禁使用,计算金额出错 | 金额、财务计算唯一推荐写法 |
| 示例缺陷 | new BigDecimal(0.1)≠0.1 | new BigDecimal ("0.1") 精准存储 0.1 |
28. 日志级别 info vs debug
表格
| 对比维度 | debug 级别 | info 级别 |
|---|---|---|
| 打印时机 | 开发环境开启,生产关闭 | 生产环境默认正常输出 |
| 日志内容 | 详细中间变量、调试过程 | 正常业务流程、关键节点记录 |
| 性能影响 | 生产关闭无字符串拼接开销 | 生产持续输出 IO,少量性能损耗 |
| 使用场景 | 调试排查问题,临时打印 | 业务正常流程、操作记录留存审计 |
29. 递归实现 vs 循环迭代实现
表格
| 对比维度 | 递归 | 循环迭代 |
|---|---|---|
| 内存开销 | 每次调用新建栈帧,深度大栈溢出 | 固定局部变量,栈内存稳定 |
| 代码简洁度 | 树形结构、分治算法代码极简 | 多层循环嵌套代码冗长 |
| 性能损耗 | 方法调用栈频繁创建销毁,开销大 | 无方法调用,执行速度更快 |
| 深度限制 | 递归深度受限 JVM 栈大小 | 无深度上限,仅受堆内存限制 |
30. 数据库 IN 查询分批 vs 一次性传入全量 ID
表格
| 对比维度 | 分批分片 IN 查询 | 一次性全量 IN 查询 |
|---|---|---|
| SQL 性能 | IN 元素少,索引生效快 | IN 上万元素索引失效,全表扫描 |
| 内存占用 | 每次加载少量数据,堆平稳 | 一次性加载百万数据,内存瞬间冲高 OOM |
| 数据库限制 | 规避数据库 IN 长度限制 | 超出数据库 SQL 长度直接抛语法异常 |
| 网络传输 | 多次少量往返 | 单次大批量数据传输 |
1944



被折叠的 条评论
为什么被折叠?



