👉 写代码时,总记不住 String 的 substring() 是左闭右开还是左闭右闭?

👉 想格式化日期,结果 SimpleDateFormat 一用多线程就出 bug?

👉 面试官问:“说说 Java 常用类?” 你脑子里闪过 String、Math,但说不完整,更别说原理了……

一、java.lang.String:字符串王者🎯 核心特性不可变性(Immutable):一旦创建,内容不可变字符序列:基于 char[] 存储(Java 9+ 用 byte[] 优化)常量池:相同字符串只存一份,节省内存代码语言:javascript复制String a = "hello";

String b = "hello";

System.out.println(a == b); // true(常量池)✅ 常用方法方法

作用

注意

length()

返回长度

-

charAt(int)

获取指定位置字符

索引从 0 开始

substring(int, int)

截取子串

左闭右开!substring(0,3) 取 0,1,2

indexOf(String)

查找子串位置

找不到返回 -1

startsWith() / endsWith()

判断前缀/后缀

-

trim()

去除首尾空格

-

toUpperCase() / toLowerCase()

大小写转换

-

split(String)

分割字符串

返回 String[]

replace(old, new)

替换字符/串

返回新字符串

❌ 经典误区:字符串拼接性能代码语言:javascript复制// 错误:频繁修改,创建大量中间对象

String s = "";

for (int i = 0; i < 1000; i++) {

s += i; // 每次都 new String()

}

// 正确:用 StringBuilder

StringBuilder sb = new StringBuilder();

for (int i = 0; i < 1000; i++) {

sb.append(i);

}

String result = sb.toString(); ✅ 结论:循环拼接用 StringBuilder,单次操作用 + 也可。

二、java.lang.StringBuilder & StringBuffer🎯 核心作用:高效字符串拼接类

线程安全

性能

使用场景

StringBuilder

❌ 不安全

⚡️ 高

单线程(推荐)

StringBuffer

✅ 安全

🔼 稍低

多线程(极少用)

代码语言:javascript复制StringBuilder sb = new StringBuilder("Hello");

sb.append(" ").append("World"); // 链式调用

sb.insert(5, " Java"); // Hello Java World

sb.delete(0, 5); // 删除 [0,5)

System.out.println(sb); // 输出: Java World 💡 建议:99% 场景用 StringBuilder。

三、java.util.List:有序可重复集合🎯 核心实现类1. ArrayList:动态数组查询快(O(1)),增删慢(O(n),涉及移动)内部基于 Object[],自动扩容(1.5倍)非线程安全代码语言:javascript复制List list = new ArrayList<>();

list.add("A");

list.add(1, "B"); // 插入

String first = list.get(0); // 查询

list.remove("A"); // 删除2. LinkedList:双向链表增删快(O(1),已知节点),查询慢(O(n))适合频繁插入/删除的场景也实现了 Deque,可作队列/栈代码语言:javascript复制List list = new LinkedList<>();

((LinkedList) list).addFirst("A"); // 头插

((LinkedList) list).addLast("B"); // 尾插 ✅ 选型建议:

多查询、少增删 → ArrayList频繁头尾增删 → LinkedList四、java.util.Map:键值对集合🎯 核心实现类1. HashMap:最常用基于 数组 + 链表/红黑树(JDK 8+)允许 null 键和 null 值非线程安全无序(插入顺序不保证)代码语言:javascript复制Map map = new HashMap<>();

map.put("Alice", 25);

map.put("Bob", 30);

Integer age = map.get("Alice"); // 获取

boolean hasKey = map.containsKey("Bob");

map.remove("Alice");2. LinkedHashMap:有序 HashMap维护插入顺序或访问顺序适合做 LRU 缓存代码语言:javascript复制// 按访问顺序排序(最近访问的放最后)

Map map = new LinkedHashMap<>(16, 0.75f, true);3. TreeMap:有序 Map基于红黑树,按键自然排序或自定义排序适合需要排序的场景代码语言:javascript复制Map map = new TreeMap<>(); // 按 key 字典序排序4. ConcurrentHashMap:线程安全 Map高并发场景首选分段锁(JDK 7)或 CAS + synchronized(JDK 8+)代码语言:javascript复制ConcurrentHashMap map = new ConcurrentHashMap<>();

map.put("count", 1);

map.get("count"); ✅ 选型建议:

普通场景 → HashMap需要顺序 → LinkedHashMap 或 TreeMap高并发 → ConcurrentHashMap五、java.util.Date & java.time:日期时间处理🎯 老日期类(不推荐)Date:可读性差,方法过时SimpleDateFormat:非线程安全!多线程共享会出错代码语言:javascript复制// ❌ 危险!多线程下可能抛异常或解析错误

private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

// ✅ 正确:每次新建,或用 ThreadLocal,或用 DateTimeFormatter🎯 新日期 API(Java 8+,强烈推荐)在 java.time 包中:

用途

LocalDateTime

日期 + 时间(无时区)

LocalDate

仅日期

LocalTime

仅时间

ZonedDateTime

带时区的日期时间

Duration

时间段(秒、纳秒)

Period

日期段(年、月、日)

DateTimeFormatter

格式化工具(线程安全)

代码语言:javascript复制// 当前时间

LocalDateTime now = LocalDateTime.now();

// 解析字符串

LocalDateTime dt = LocalDateTime.parse("2025-08-13T10:30:00");

// 格式化

String str = now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm"));

// 计算

LocalDateTime tomorrow = now.plusDays(1);

Duration duration = Duration.between(now, tomorrow); ✅ 结论:新项目一律用 java.time,安全、清晰、功能强。

六、java.math:精确计算1. BigInteger:大整数代码语言:javascript复制BigInteger a = new BigInteger("12345678901234567890");

BigInteger b = new BigInteger("98765432109876543210");

BigInteger sum = a.add(b); // 加法2. BigDecimal:高精度浮点数(金融首选!)代码语言:javascript复制BigDecimal price = new BigDecimal("19.9");

BigDecimal tax = new BigDecimal("0.05");

BigDecimal total = price.multiply(tax.add(BigDecimal.ONE));

// 保留2位小数,四舍五入

total = total.setScale(2, RoundingMode.HALF_UP);⚠️ 绝对不要用 double 做金钱计算!

代码语言:javascript复制System.out.println(0.1 + 0.2); // 输出 0.30000000000000004!七、java.util.Objects:空值工具类代码语言:javascript复制String name = null;

// 判空(比 == null 更优雅)

boolean isNull = Objects.isNull(name);

boolean nonNull = Objects.nonNull(name);

// 防空指针(常用)

String displayName = Objects.requireNonNull(name, "名字不能为空");

// 生成 hashCode(处理 null)

int hash = Objects.hash(name, age);

// 比较(处理 null)

int cmp = Objects.compare("a", "b", String::compareTo);八、高频问题 & 高分回答Q1: String 为什么设计成不可变的? 答:

安全性:防止被恶意修改(如作为 HashMap 的 key);线程安全:不可变对象天然线程安全;缓存 hash 值:hashCode() 只计算一次,提升性能;字符串常量池:实现基础,相同字符串可共享。Q2: ArrayList 扩容机制是怎样的? 答:

默认容量 10;扩容时,新容量 = 原容量 × 1.5;使用 Arrays.copyOf() 复制数据;扩容是耗时操作,建议初始化时指定合理容量。Q3: HashMap 的底层原理? 答:

JDK 8+:

基于 数组 + 链表/红黑树;通过 hash(key) 计算桶位置;链表长度 > 8 且数组长度 ≥ 64 时,转为红黑树;扩容时 rehash,JDK 8 优化了迁移过程。Q4: BigDecimal 为什么能精确计算? 答:

它把浮点数拆成 unscaledValue(BigInteger) 和 scale(小数位数)。

比如 19.9 存储为 199 和 scale=1,所有计算基于整数,避免了二进制浮点数的精度丢失问题。

✅ 总结:一张表搞懂常用类选型需求

推荐类

关键点

字符串操作

String + StringBuilder

String 不可变,拼接用 StringBuilder

有序集合

ArrayList

查询多用它,注意初始化容量

键值存储

HashMap / ConcurrentHashMap

普通用前者,并发用后者

日期时间

java.time.LocalDateTime

线程安全,API 清晰

金钱计算

BigDecimal

绝对不用 double!

判空工具

Objects

requireNonNull 防空指针

🔚 最后一句话 Java 常用类不是“语法糖”,而是“生产力工具”。

从 String 的不可变性,到 ConcurrentHashMap 的高并发设计,

每一个类背后都是对性能、安全、易用性的深度权衡。

掌握它们,你才能写出高效、健壮、专业的 Java 代码!

希望这篇能帮你系统掌握 Java 常用类!