Java 核心类库详解

Java 没有脚本语言那样的全局内置函数——对应物是 java.lang 与 java.util 的核心类库。本篇按「每个类一个词条」覆盖最常用 API,每条给签名与坑。

1. Object – 万物基类与 equals/hashCode 契约

描述

所有类的根。四个关键方法:equals(内容相等)、hashCode(哈希)、toString(文本表示)、getClass(运行时类型)。契约:equals 相等则 hashCode 必须相等,否则 HashMap/HashSet 会静默失效。

语法和示例

class Money {
    private final long cents;
    Money(long c) { cents = c; }
    @Override public boolean equals(Object o) {
        if (this == o) return true;                     // 快路径
        if (!(o instanceof Money m)) return false;      // 16+ 模式匹配
        return cents == m.cents;
    }
    @Override public int hashCode() {
        return Long.hashCode(cents);                    // 必须与 equals 一致
    }
    @Override public String toString() {
        return "Money(" + cents + ")";
    }
}
new Money(100).equals(new Money(100));   // true
new Money(100).hashCode() == new Money(100).hashCode();  // true

注意事项

  • 只重写 equals 不重写 hashCode:HashSet 里同一值会存两份,HashMap get 返回 null
  • equals 必须满足自反/对称/传递/一致/非空五条契约
  • hashCode 用 Objects.hash(a, b) 或 Long/Integer.hashCode(x),别手写乘法易溢出
  • toString 默认输出「类名@十六进制哈希」,日志里无法读——生产必重写
  • clone() 是浅拷贝且需实现 Cloneable,优先用拷贝构造器或 record

2. String – 不可变字符串

描述

不可变字符序列,任何「修改」都返回新对象。字面量进常量池(同内容复用同一对象)。方法极多,这里按用途分组。

语法和示例

String s = "Hello";
// 判等与查找
s.equals("Hello"); s.equalsIgnoreCase("HELLO");
s.isEmpty(); s.isBlank();                    // isBlank 认全空白(11+)
s.indexOf("l"); s.lastIndexOf("l");
s.contains("ell"); s.startsWith("He"); s.endsWith("lo");
// 截取与转换
s.substring(1); s.substring(1, 3);           // 含头不含尾
s.replace("l", "L"); s.replaceAll("\\d", "#");
s.toUpperCase(); s.trim(); s.strip();        // strip 认 Unicode 空白(11+)
s.repeat(3); s.lines();                      // 11+
// 拆分与连接
"a,b,c".split(",");  String.join("-", "a", "b");
// 编码
s.getBytes(StandardCharsets.UTF_8);
new String(bytes, StandardCharsets.UTF_8);
// 格式化
String.format("%s=%d", "k", 1);

注意事项

  • 判等永远用 equals 不用 ==(== 比引用,字面量碰巧相等,new String 就不等了)
  • substring 在 Java 7 后是真正的拷贝(不复用底层数组,无内存泄漏问题)
  • 循环内用 + 拼接会多次创建对象——把 StringBuilder 提到循环外
  • split 的参数是正则:split(“.”) 会按任意字符切,切点号要 split(“\\.”)
  • String 的 length() 是 UTF-16 代码单元数,emoji 算 2;要字符数用 codePointCount

3. StringBuilder / StringBuffer – 可变字符串

描述

StringBuilder 非线程安全但快(单线程首选);StringBuffer 加 synchronized(多线程共享时才用)。二者 API 一致。

语法和示例

var sb = new StringBuilder(64);              // 预估容量避免扩容
sb.append("a").append(1).append(',');
sb.insert(0, "前").deleteCharAt(0);
sb.replace(0, 1, "X");
sb.reverse();
sb.length(); sb.setLength(0);                 // 复用清空(比 new 省)
sb.toString();
// 循环拼接的正确姿势
var out = new StringBuilder();
for (var w : words) out.append(w).append('\n');
// String.join 更简洁(无中间状态时优先)
String.join(", ", list);

注意事项

  • 单线程场景永远用 StringBuilder,StringBuffer 的锁开销白给
  • 初始容量给足(预估最终长度)能避免多次数组扩容拷贝
  • setLength(0) 复用实例,但注意多线程共享时仍不安全
  • append(char) 比 append(String) 快,别为单个字符建字符串
  • 链式 append 返回 this,可连续调用,但别在 lambda 里共享 builder

4. Integer/Long/Double – 包装类

描述

基本类型的对象包装。关键点:自动装箱/拆箱、缓存范围、parseXxx 与 valueOf 的区别。用于泛型、集合、可空场景。

语法和示例

Integer.parseInt("42");                      // 返回 int
Integer.parseInt("ff", 16);                   // 255
Integer.valueOf("42");                        // 返回 Integer(走缓存)
Integer i = 42;                               // 自动装箱
int n = i;                                    // 自动拆箱
Integer.MAX_VALUE; Integer.MIN_VALUE;
Integer.compare(a, b);                        // -1/0/1(避免 a-b 溢出)
Integer.toBinaryString(255); Long.toHexString(255L);
Double.isNaN(x); Double.isInfinite(x);
Integer.toString(42); String.valueOf(42);
// 缓存陷阱
Integer a = 127, b = 127; a == b;             // true(缓存 -128~127)
Integer c = 128, d = 128; c == d;             // false!
Boolean.TRUE; Character.valueOf('a');

注意事项

  • == 比较包装类只在缓存范围(-128~127)内碰巧为 true——永远用 equals
  • 为 null 的包装类自动拆箱会抛 NPE(三元表达式混用 Integer/int 时尤其隐蔽)
  • 集合里存基本类型只能装箱,大数量(如百万 Integer)内存是 int[] 的 4-6 倍
  • valueOf 走缓存,new Integer()(已废弃)不缓存——用 valueOf 或自动装箱
  • parseInt 遇溢出抛 NumberFormatException,超 long 范围用 BigInteger

5. Math – 数学函数

描述

全静态工具类。注意负数取整与除法的语义差异:/ 向零取整,floorDiv 向下取整。溢出用 addExact 系列检测。

语法和示例

Math.abs(-3); Math.abs(Integer.MIN_VALUE);   // 后者仍是负数(溢出)
Math.max(1, 2); Math.min(3L, 4L);
Math.floor(1.8);   // 1.0  向下
Math.ceil(1.2);    // 2.0  向上
Math.round(1.5);   // 2    四舍五入(.5 向正无穷)
Math.round(-1.5);  // -1   !不是 -2
Math.pow(2, 10); Math.sqrt(16); Math.cbrt(27);
Math.hypot(3, 4);                            // 5.0 斜边(防溢出)
Math.floorDiv(-7, 2);                        // -4  向下取整除
Math.floorMod(-7, 2);                        // 1   符号跟除数
Math.addExact(Integer.MAX_VALUE, 1);         // 抛 ArithmeticException
Math.multiplyExact(a, b);
Math.toRadians(180);

注意事项

  • Math.round 用「四舍五入到正无穷」,负数 .5 会往大的取(-1.5 → -1)
  • Math.abs(Integer.MIN_VALUE) 返回仍是负数——对绝对值负的 int 无解,先转 long
  • 金额/精确计算不用 Math,用 BigDecimal
  • addExact/multiplyExact 比事后检查更可靠,适合外部输入参与的运算
  • Math.random() 返回 [0,1) double,线程共享 Random 有竞争——并发用 ThreadLocalRandom

6. Arrays – 数组工具

描述

排序、查找、填充、拷贝、比较、转字符串。binarySearch 要求数组已排序,否则结果未定义。

语法和示例

int[] a = {3, 1, 2};
Arrays.sort(a);                              // [1,2,3]
Arrays.sort(a, 1, 3);                        // 只排区间
Arrays.toString(a);
Arrays.binarySearch(a, 2);                   // 需先排序
Arrays.copyOf(a, 5);                         // 扩容(补 0)
Arrays.copyOfRange(a, 0, 2);
Arrays.fill(a, 0);
Arrays.equals(a, b); Arrays.hashCode(a);
int[][] g = new int[3][4];                   // 二维数组
Arrays.deepToString(g);                      // 多维用 deep
Arrays.stream(a).sum();
// 对象数组按字段排序
Arrays.sort(users, Comparator.comparing(User::age));

注意事项

  • binarySearch 未排序时返回结果不可信(可能负值也可能误命中)
  • Arrays.asList 返回定长视图,add/remove 抛 UnsupportedOperationException;要可变用 new ArrayList(Arrays.asList(…))
  • 基本类型数组用 Arrays.sort 是双轴快排;对象数组是归并(稳定)
  • Arrays.asList(int[]) 会得到 List 而不是 List(基本类型不装箱)
  • copyOf 长度变大时新元素是默认值(0/false/null)

7. Collections – 集合工具

描述

静态工具类:排序、反转、打乱、不可变视图、查找极值、频率统计。

语法和示例

List<String> l = new ArrayList<>(List.of("b", "a"));
Collections.sort(l);
Collections.reverse(l);
Collections.shuffle(l);
Collections.swap(l, 0, 1);
Collections.max(l); Collections.min(l);
Collections.frequency(l, "a");
Collections.binarySearch(sortedList, "x");
Collections.unmodifiableList(l);             // 只读视图(原表仍可变)
Collections.synchronizedList(l);             // 线程安全包装(粗粒度锁)
Collections.emptyList(); Collections.singletonList("x");
Collections.nCopies(3, "x");

注意事项

  • unmodifiable 是视图不是拷贝——原集合被改时只读视图内容也跟着变
  • synchronizedList 只保证单方法原子,复合操作用 synchronized(list) 包
  • Collections.sort 已可被 list.sort() 替代(Java 8+)
  • emptyList/singletonList 是不可变且共享实例,别往里 add
  • 9+ 优先用 List.of/Map.of(不可变工厂),比 Collections 包装更轻

8. ArrayList – 动态数组

描述

随机访问 O(1),尾部追加均摊 O(1),中间插删 O(n)。默认容量 10,扩容约 1.5 倍。是 List 的事实默认实现。

语法和示例

var list = new ArrayList<String>(100);       // 预估容量
list.add("a");                               // 尾部 O(1)
list.add(0, "head");                         // 中间 O(n)
list.get(0); list.set(0, "A");
list.remove(0);                              // 按下标
list.remove("a");                            // 按值(首次匹配)
list.size(); list.isEmpty(); list.clear();
list.contains("a");                          // O(n)
for (String s : list) {}
list.removeIf(s -> s.isEmpty());
list.subList(0, 2);                          // 视图!
List<String> copy = new ArrayList<>(list);

注意事项

  • remove(int) 与 remove(Object) 是两个重载:list.remove(1) 删下标 1,删值 “1” 要 remove(“1”)
  • subList 返回视图,原 list 结构变动后视图失效(ConcurrentModificationException)
  • for 循环里 remove 会抛异常——用 iterator.remove 或 removeIf
  • 预估容量能避免多次扩容拷贝,批量添加时收益明显
  • ArrayList 不是线程安全的;并发用 CopyOnWriteArrayList(读多写少)或加锁

9. HashMap – 哈希映射

描述

键值对,get/put 均摊 O(1)。允许一个 null 键与多个 null 值。不保证顺序(遍历顺序不稳定)。键必须正确实现 hashCode/equals。

语法和示例

var m = new HashMap<String, Integer>();
m.put("a", 1);
m.get("a");                                  // 1
m.getOrDefault("b", 0);
m.putIfAbsent("c", 3);
m.merge("a", 1, Integer::sum);               // 计数惯用法
m.computeIfAbsent("k", this::load);          // 缓存惯用法
m.compute("x", (k, v) -> v == null ? 1 : v + 1);
m.containsKey("a"); m.containsValue(1);
m.remove("a");
for (var e : m.entrySet()) { e.getKey(); e.getValue(); }
m.keySet(); m.values();
new HashMap<>(16, 0.75f);                    // 容量与负载因子
// 保序替代
var ordered = new LinkedHashMap<String, Integer>();
var sorted = new TreeMap<String, Integer>();

注意事项

  • 键是可变对象(如 ArrayList)且改内容后,hashCode 变化会导致 get 找不到
  • 遍历时 remove 抛 ConcurrentModificationException——用 entrySet().iterator().remove()
  • 并发场景用 ConcurrentHashMap;Collections.synchronizedMap 锁太粗
  • 负载因子默认 0.75,容量应为预计元素数的 1.5 倍左右(避免频繁 rehash)
  • TreeMap 要求键可比较(Comparable 或传 Comparator),不允许 null 键

10. List.of / Map.of / Set.of – 不可变工厂(9+)

描述

一行构造不可变集合,不接受 null,任何修改抛 UnsupportedOperationException。适合常量与配置,比 Collections 包装更轻量。

语法和示例

List<String> xs = List.of("a", "b");
Map<String, Integer> m = Map.of("go", 1, "php", 2);
Set<Integer> s = Set.of(1, 2, 3);
Map<String, Integer> m10 = Map.ofEntries(
    Map.entry("a", 1), Map.entry("b", 2));
// 拷贝可变集合为不可变
List.copyOf(mutableList);
Map.copyOf(mutableMap);
Set.copyOf(mutableSet);
// 取第一个元素
xs.getFirst(); xs.getLast();                 // 21+ SequencedCollection

注意事项

  • List.of(null) 直接抛 NPE——不可变集合禁止 null 元素
  • Map.of 最多 10 对,超出用 Map.ofEntries
  • 不可变是深还是浅?只保证集合本身结构不可变,元素对象仍可改
  • List.copyOf 若入参已是不可变则直接返回原引用(不拷贝)
  • 顺序:List.of 保参数顺序;Set.of 不保证顺序,Map.of 不保证

11. Optional – 可能缺席的容器

描述

表达「有值或无值」的返回值容器。设计意图是替代返回 null,不是替代所有判空。

语法和示例

Optional<User> u = repo.findById(id);
u.isPresent(); u.isEmpty();
u.get();                                     // 不推荐裸用
u.orElse(new User());
u.orElseGet(() -> createDefault());          // 惰性
u.orElseThrow(() -> new NotFoundException(id));
u.map(User::name);                           // 有值才映射
u.filter(User::isActive);
u.ifPresent(this::send);                     // 有值才执行
u.ifPresentOrElse(this::send, this::skip);
repo.find(id).or(() -> repo.findBackup(id)); // 备用来源
Optional.of("x");                            // 不能为 null(否则 NPE)
Optional.ofNullable(maybeNull);

注意事项

  • 不要用作字段/方法参数/集合元素(只适合返回值)
  • get() 与 isPresent() 组合等于老式判空,没有发挥 Optional 价值——用 map/orElseThrow
  • Optional 是值对象,不能序列化(别放 DTO 字段)
  • 集合不应返回 Optional,返回空集合更清晰
  • Optional 的链式调用本身有对象开销,高频路径慎用

12. Stream – 声明式管道

描述

集合的函数式操作链。中间操作惰性(不触发计算),终端操作才执行一次。流只能消费一次。

语法和示例

List<String> top = orders.stream()
    .filter(o -> o.amount() > 100)
    .sorted(Comparator.comparing(Order::amount).reversed())
    .limit(5)
    .map(Order::customer)
    .toList();                               // 16+(不可变)
// 分组与统计
Map<String, List<Order>> byCust = orders.stream()
    .collect(Collectors.groupingBy(Order::customer));
Map<String, Long> counts = orders.stream()
    .collect(Collectors.groupingBy(Order::customer, Collectors.counting()));
String joined = names.stream().collect(Collectors.joining(", "));
int total = orders.stream().mapToInt(Order::amount).sum();
IntSummaryStatistics st = orders.stream()
    .mapToInt(Order::amount).summaryStatistics();
// 扁平化
Stream.of(List.of(1,2), List.of(3)).flatMap(List::stream).toList();
// 创建流
Stream.of("a","b"); Arrays.stream(arr); IntStream.range(0, 10);
Files.lines(path); Stream.iterate(0, x -> x + 1).limit(10);

注意事项

  • 流只能消费一次,终端操作后再用会抛 IllegalStateException
  • 中间操作惰性——没有终端操作时什么都不会发生(调试常见困惑)
  • parallelStream 只适合大数据量 CPU 密集;小数据或 IO 会变慢
  • 在流里修改外部可变集合是反模式(并发流更危险)
  • mapToInt 等原始流避免装箱,大数据量性能差异显著

13. Comparator – 比较器

描述

定义排序规则。comparing 链式组合、thenComparing 处理多级排序、reversed 反转、nullsFirst/Last 处理空值。

语法和示例

Comparator<User> byName = Comparator.comparing(User::name);
users.sort(byName);
// 多级
users.sort(Comparator.comparing(User::dept)
    .thenComparing(User::age, Comparator.reverseOrder())
    .thenComparing(User::name));
// 反转
Comparator<User> desc = byName.reversed();
// 空值安全
Comparator.comparing(User::name,
    Comparator.nullsLast(Comparator.naturalOrder()));
// 原始类型避免装箱
Comparator.comparingInt(String::length);
// 自定义
Comparator<String> byLen = (a, b) -> Integer.compare(a.length(), b.length());

注意事项

  • (a, b) -> a – b 有整数溢出风险,用 Integer.compare(a, b)
  • reversed() 作用于整条链时需要括号注意优先级:comparing(…).reversed() 只反最后一个键
  • thenComparing 的第二个键用 reverseOrder 才是独立反转,别整体 reversed
  • Collections.sort 与 list.sort 都稳定排序(相等元素保原序)
  • 对 null 排序必须显式 nullsFirst/Last,否则 NPE

14. java.time – 日期时间 API

描述

不可变、线程安全的时间 API。四大类型:LocalDate(无时间)、LocalTime、LocalDateTime(无时区)、ZonedDateTime/Instant(有时区/绝对时刻)。

语法和示例

LocalDate d = LocalDate.of(2026, 10, 29);
LocalDate.parse("2026-10-29");               // ISO 默认
LocalDateTime dt = LocalDateTime.of(d, LocalTime.of(15, 30));
Instant now = Instant.now();                 // UTC 绝对时刻
ZonedDateTime z = dt.atZone(ZoneId.of("Asia/Shanghai"));
// 运算(都返回新对象)
d.plusDays(7).minusMonths(1);
dt.plus(Duration.ofMinutes(30));
Duration.between(t1, t2).toMinutes();        // 时刻差
Period.between(d1, d2).getYears();           // 日期差
// 格式化
dt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
DateTimeFormatter.ISO_LOCAL_DATE;
// 比较
d.isBefore(e); d.isAfter(e); d.isEqual(e);
// 转旧 API
java.util.Date legacy = java.util.Date.from(now);

注意事项

  • LocalDateTime 不含时区——存数据库/跨时区比较必须用 Instant 或 ZonedDateTime
  • DateTimeFormatter 线程安全(老的 SimpleDateFormat 不是),可作静态常量复用
  • 月份从 1 开始(不是老的 Calendar 从 0),别写错
  • Duration 是时间量(时分秒),Period 是日期量(年月日),别混用
  • 系统默认时区随环境变——跨时区业务显式指定 ZoneId

15. BigDecimal – 精确小数

描述

任意精度十进制,金额/汇率/税率必须用它。不可变,运算返回新对象。构造器必须用字符串。

语法和示例

var price = new BigDecimal("19.99");         // 必须字符串构造
var tax = new BigDecimal("0.13");
var total = price.multiply(BigDecimal.ONE.add(tax))
    .setScale(2, RoundingMode.HALF_UP);
price.add(new BigDecimal("0.01"));
price.subtract(qty); price.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);
price.compareTo(other) == 0;                 // 比大小
price.equals(new BigDecimal("19.990"));      // false!equals 连 scale 比
price.stripTrailingZeros();
price.toPlainString();                       // 不用科学计数法
price.setScale(2, RoundingMode.HALF_UP);

注意事项

  • new BigDecimal(0.1) 会得到 0.1000000000000000055511…,必须 new BigDecimal(“0.1”)
  • equals 比较时连 scale 一起比(19.9 与 19.90 不等),比大小用 compareTo
  • divide 不指定 scale 且除不尽时抛 ArithmeticException
  • BigDecimal 不可变,每次运算产生新对象——大循环里注意开销
  • toString 可能输出科学计数法,展示/存储用 toPlainString

16. Objects – 空安全工具

描述

静态工具:requireNonNull 快速校验、equals/hash 空安全、requireNonNullElse 提供默认值。

语法和示例

Objects.requireNonNull(user, "user 必填");   // null 抛 NPE 带消息
Objects.requireNonNullElse(input, "默认值");
Objects.requireNonNullElseGet(input, () -> compute());
Objects.equals(a, b);                        // 两个 null 也算相等
Objects.hash(a, b, c);                       // 生成 hashCode
Objects.hashCode(x);
Objects.isNull(x); Objects.nonNull(x);       // 可作方法引用
// 方法引用场景
list.stream().filter(Objects::nonNull);

注意事项

  • requireNonNull 抛 NPE 而非 IllegalArgumentException——想用后者需自己抛
  • Objects.equals 只做 equals 调用,不做类型转换
  • Objects.hash 每次创建数组有开销,高频路径手写更划算
  • isNull/nonNull 主要为方法引用存在,普通判空用 x == null 更直观

17. Random / ThreadLocalRandom / SecureRandom

描述

三个随机源:Random(可复现,多线程共享有竞争)、ThreadLocalRandom(并发首选)、SecureRandom(密码学安全,慢)。

语法和示例

var r = new Random(42);                      // 种子可复现
r.nextInt(); r.nextInt(100);                 // [0,100)
r.nextLong(); r.nextDouble(); r.nextBoolean();
ThreadLocalRandom.current().nextInt(1, 101); // 并发场景
SecureRandom sr = new SecureRandom();        // 令牌/盐
sr.nextInt(1000);
// 唯一 ID
UUID.randomUUID().toString();

注意事项

  • Random 实例被多线程共享时性能下降且结果有竞争——并发用 ThreadLocalRandom
  • 密码、令牌、盐必须用 SecureRandom,普通 Random 可预测
  • Math.random() 内部是共享 Random,别在并发热点用
  • nextInt(bound) 不含 bound,要含用 nextInt(a, b)
  • 固定种子的 Random 结果可复现(测试友好),但绝不能用于安全场景

18. Record – 不可变数据载体(16+)

描述

一行声明不可变数据类,自动生成构造器、equals/hashCode/toString、访问器。可带紧凑构造器校验、静态工厂、额外方法。

语法和示例

record Point(int x, int y) {
    Point {                                  // 紧凑构造器
        if (x < 0) throw new IllegalArgumentException("x 不能为负");
    }
    static Point origin() { return new Point(0, 0); }
    int sum() { return x + y; }
}
var p = new Point(1, 2);
p.x(); p.y();                                // 访问器(不是 getX)
p.equals(new Point(1, 2));                   // true
new Point(1, 2, 3);                          // 编译错:只有两个组件
// 泛型 record
record Pair<A, B>(A left, B right) {}
// 实现接口
record Range(int lo, int hi) implements Comparable<Range> {
    public int compareTo(Range o) { return Integer.compare(lo, o.lo); }
}

注意事项

  • record 不可继承,也不能被继承(没有 extends 空间)
  • 组件字段隐式 private final,无 setter——要改只能造新实例
  • record 不能声明实例字段(只能是组件),但可有静态字段
  • 紧凑构造器里赋值可用字段名直接引用(无需 this.x = x)
  • record 适合 DTO/值对象;有可变状态或大量派生属性时用普通类
上一篇
下一篇