第31.1篇:Java常用API与底层原理对比集(2026版)
📌系列导航:《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第31篇:Java数组详解 |
➡️ 下一篇:第32篇:Java常用工具类
🗺️ 本文阅读地图(2 分钟速览)
常用 API 是开发中的“日常工具”,也是面试中容易忽视的考点。本文汇总 10 组高频易混淆概念,每组配一句话总结 + 详细对比:
| 编号 | 对比组 | 一句话区分 |
|---|---|---|
| 1 | String vs StringBuilder vs StringBuffer | String 不可变,Builder 快(线程不安全),Buffer 慢(线程安全) |
| 2 | 字符串常量池 vs 堆 | 字面量在池中复用,new String()在堆中新建 |
| 3 | wait()vssleep() | wait 释放锁需 notify,sleep 不释放锁时间到自醒 |
| 4 | notify()vsnotifyAll() | 唤醒一个 vs 唤醒全部 |
| 5 | EnumMapvsHashMap | EnumMap 专为枚举设计,效率更高 |
| 6 | DatevsLocalDate | Date 旧(可变/线程不安全),LocalDate 新(不可变/线程安全) |
| 7 | SimpleDateFormat线程安全问题 | 内部有 Calendar 共享状态,多线程需注意 |
| 8 | 直接缓冲区 vs 堆缓冲区 | 直接缓冲区减少拷贝(适合网络),堆缓冲区受 GC 管理 |
| 9 | flip()vsrewind()vsclear() | flip 写→读,rewind 重读,clear 重置 |
| 10 | 枚举 vs 普通常量类 | 枚举类型安全,支持方法、单例 |
文章目录
- 第31.1篇:Java常用API与底层原理对比集(2026版)
- 🗺️ 本文阅读地图(2 分钟速览)
- 一、核心知识点概览
- 二、逐组对比详解
- 对比1:String vs StringBuilder vs StringBuffer
- 对比2:字符串常量池 vs 堆
- 对比3:`wait()` vs `sleep()`
- 对比4:`notify()` vs `notifyAll()`
- 对比5:`EnumMap` vs `HashMap`
- 对比6:`Date` vs `LocalDate`
- 对比7:`SimpleDateFormat` 线程安全问题
- 对比8:直接缓冲区 vs 堆缓冲区
- 对比9:`flip()` vs `rewind()` vs `clear()`
- 对比10:枚举 vs 普通常量类
- 三、避坑要点
- 四、练习题
- 📊 你的学习进度
- 👉 下一篇文章预告
一、核心知识点概览
本篇文章涵盖 Java 常用 API 与底层原理中 10 组最高频的易混淆概念,来源于第21~31篇的全部内容:
| 篇号 | 涉及对比 | 数量 |
|---|---|---|
| 第21篇 | Object 方法(扩展) | — |
| 第22篇 | String vs StringBuilder vs StringBuffer、常量池 | 2组 |
| 第23篇 | 直接缓冲区 vs 堆缓冲区、flip/rewind/clear | 2组 |
| 第24篇 | 枚举 vs 常量类、EnumMap vs HashMap | 2组 |
| 第26篇 | 注解(扩展) | — |
| 第27篇 | 代理(扩展) | — |
| 第28篇 | 反射(扩展) | — |
| 第29-30篇 | Date vs LocalDate、SimpleDateFormat 线程安全 | 2组 |
| 第31篇 | 数组(扩展) | — |
二、逐组对比详解
对比1:String vs StringBuilder vs StringBuffer
| 对比维度 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 可变性 | ❌ 不可变 | ✅ 可变 | ✅ 可变 |
| 线程安全 | ✅ 安全(不可变) | ❌不安全 | ✅安全(synchronized) |
| 性能 | 拼接时低 | 最高 | 较低(同步开销) |
| 适用场景 | 少量拼接/不可变 | 单线程、频繁拼接 | 多线程、频繁拼接 |
| 引入版本 | Java 1.0 | Java 5 | Java 1.0 |
💡 一句话总结:String 不可变(安全但拼接慢),StringBuilder 单线程神器,StringBuffer 多线程安全但慢。
对比2:字符串常量池 vs 堆
| 对比维度 | 字符串常量池 | 堆 |
|---|---|---|
| 存储位置 | 方法区(元空间) | 堆 |
| 存储内容 | 字面量字符串(复用) | new String()创建的对象 |
| 是否复用 | ✅ 相同字符串共用一份 | ❌ 每次new创建新对象 |
| 垃圾回收 | 可回收但频率低 | 正常 GC 回收 |
代码验证:
Strings1="hello";// 常量池Strings2="hello";// 复用池中对象Strings3=newString("hello");// 堆中新建System.out.println(s1==s2);// trueSystem.out.println(s1==s3);// false对比3:wait()vssleep()
| 对比维度 | wait() | sleep() |
|---|---|---|
| 所属类 | Object 实例方法 | Thread 静态方法 |
| 是否释放锁 | ✅释放锁 | ❌不释放锁 |
| 唤醒方式 | notify()/notifyAll() | 时间到自动醒来 |
| 使用位置 | 必须在synchronized中 | 任何地方 |
| 线程状态 | WAITING | TIMED_WAITING |
对比4:notify()vsnotifyAll()
| 对比维度 | notify() | notifyAll() |
|---|---|---|
| 唤醒数量 | 唤醒一个等待线程 | 唤醒所有等待线程 |
| 选择哪个线程 | JVM 随机选择 | 全部唤醒 |
| 适用场景 | 一个生产者一个消费者 | 多个生产者/消费者 |
对比5:EnumMapvsHashMap
| 对比维度 | EnumMap | HashMap |
|---|---|---|
| 键类型 | 只能是枚举类型 | 任意类型 |
| 底层实现 | 数组(枚举常量索引) | 数组+链表+红黑树 |
| 性能 | 更高(直接数组访问) | 较低(哈希计算) |
| null 键 | ❌ 不允许 | ✅ 允许 |
| 适用场景 | 键是枚举类型时优先使用 | 通用 Map |
对比6:DatevsLocalDate
| 对比维度 | java.util.Date | java.time.LocalDate |
|---|---|---|
| 可变性 | ❌ 可变 | ✅不可变 |
| 线程安全 | ❌ 不安全 | ✅安全 |
| 月份起始 | 从 0 开始(1月=0) | 从 1 开始(1月=1) |
| 年份 | 从 1900 开始 | 正常年份 |
| 是否推荐 | ❌ 推荐使用 | ✅推荐(Java 8+) |
对比7:SimpleDateFormat线程安全问题
问题根源:SimpleDateFormat内部维护一个Calendar对象引用,多线程共享时,一个线程修改 Calendar 状态会影响其他线程,导致解析错误或异常。
解决方案:
- ✅方案一:每次使用时创建新实例(最简单)
- ✅方案二:使用
ThreadLocal包装 - ✅方案三:使用
DateTimeFormatter(Java 8+,线程安全)
对比8:直接缓冲区 vs 堆缓冲区
| 对比维度 | 直接缓冲区 | 堆缓冲区 |
|---|---|---|
| 内存位置 | 系统本机内存(Native) | JVM 堆内存 |
| GC 管理 | ❌ 不受 GC 管理 | ✅ 受 GC 管理 |
| 数据拷贝次数 | 1 次(少一次拷贝) | 2 次(堆→Native 再传输) |
| 分配速度 | 慢(系统调用) | 快 |
| 适用场景 | 大文件、网络 I/O | 小数据、通用场景 |
对比9:flip()vsrewind()vsclear()
| 方法 | 作用 | position | limit |
|---|---|---|---|
flip() | 写→读模式切换 | 0 | 原 position |
rewind() | 重新读(不改变 limit) | 0 | 不变 |
clear() | 重置(准备写) | 0 | capacity |
对比10:枚举 vs 普通常量类
| 对比维度 | 枚举(enum) | 普通常量类 |
|---|---|---|
| 类型安全 | ✅ 编译期检查 | ❌ 可能传入非法值 |
| 方法 | ✅ 可以有方法 | ❌ 只有常量 |
| 单例 | ✅ 天然单例 | ❌ 需手动实现 |
| 反射攻击 | ✅ 防止 | ❌ 可反射修改 |
三、避坑要点
| 错误/误区 | 后果 | 正确做法 |
|---|---|---|
循环中用+拼接字符串 | 性能极差 | 用StringBuilder |
用==比较字符串内容 | 可能 false | 用equals() |
多线程共享SimpleDateFormat | 解析错误/异常 | 用ThreadLocal或DateTimeFormatter |
使用Date的废弃构造方法 | 代码不可靠 | 用LocalDate |
忘记flip()直接读 Buffer | 读不到数据 | 写后读前flip() |
四、练习题
代码题:
String s1 = "abc"; String s2 = new String("abc");s1 == s2的结果是什么?为什么?改错题:下面的代码有什么问题?如何修复?
SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd");// 多个线程共享 sdf选择题:ByteBuffer 写数据后要切换到读模式,应该调用哪个方法?
A.clear()B.rewind()C.flip()D.reset()
📊 你的学习进度
- 当前:第31.1篇 ·基础篇外挂:常用API与底层原理对比集
- ✅ 已完成:基础篇第1~31篇 + 第4.1篇 + 第20.1~20.2篇 + 第31.1篇
- 📖 正在学:第31.1篇
- ⏳ 待学习:第32~44篇(主体)+ 更多外挂篇
👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇
👉 下一篇文章预告
🚀下一篇:《第32篇:Java常用工具类》
内容简介:Objects、Collections、Arrays 工具类使用与对比。
📌《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!