Java 的核心竞争力一半在语言,一半在类库:你要的轮子,十有八九标准库里有。本章挑最常用的几件家什讲透:String 家族(含那个著名的拼接性能坑)、包装类与自动装箱(含那个 == 的续集)、Math/Random、以及新时间和旧时间的换代故事。学完这章,你写代码的速度会翻倍,踩坑率会减半。
String 的头号特征:不可变(immutable)。任何「修改」都不动原对象,只产新的:
String s = "hello";
String t = s.toUpperCase(); // s 还是 "hello",t 是新对象 "HELLO"
s = s + " world"; // s 变量指向了新对象,原来的 "hello" 原封不动
不可变带来三个直接后果。安全共享:字符串随便传(改不了),常量池能放心复用(第 8 章 == 之谜的另一半谜底:字面量在编译期进常量池,相同的字面量是同一个对象)。每次拼接都在造新对象:s += x 循环一万次,就是造一万次中间对象(旧对象全靠 GC 收尸),这是 Java 性能入门第一课。hash 缓存:String 的 hashCode 算一次存着,所以能当 HashMap 的键(第 10 章)。
常用方法速查:length()、charAt(i)、substring(b, e)(前含后不含)、indexOf、split(",")(返回 String[],参数是正则!)、trim()、contains、equals/equalsIgnoreCase、String.valueOf(x)(万物转字符串)。注意 s1 + s2 的编译器优化:单行拼接现代编译器会自动转 StringBuilder,但循环内拼接必须自己显式 StringBuilder(优化器不做跨迭代的优化):
StringBuilder sb = new StringBuilder(); // 可变字符串,拼接专用
for (String w : words) sb.append(w).append(' ');
String s = sb.toString();
// StringBuffer = 线程安全版(方法全加锁),日常用 StringBuilder 即可
和 C 的 char* 手动管理(指针课第 6 章的 '\0' 血泪)相比,String 把内存、长度、编码全管了;不可变设计则和 Python 的 str 同款。
集合类只收对象(第 10 章),基本类型 int 进不了集合,于是每个基本类型配了一个包装类(Integer、Double、Boolean……),并配自动装箱/拆箱:
Integer x = 10; // 自动装箱:int → Integer(编译器插了 Integer.valueOf(10))
int y = x + 1; // 自动拆箱:Integer → int(x.intValue() + 1)
方便的代价是两个陷阱。陷阱一:== 的老坑换马甲。Integer 缓存了 -128 到 127 的对象,这个范围内的 == 比较碰巧 true(同一缓存对象),范围外就是 false:
Integer a = 127, b = 127;
System.out.println(a == b); // true(缓存,同一对象)
Integer c = 128, d = 128;
System.out.println(c == d); // false!两个独立对象
「面试题答 true 还是 false」不重要,纪律重要:包装类比较内容永远 equals,== 只用来判断 null。陷阱二:NPE 新产地。Integer x = null; int y = x; 拆箱 null 直接 NullPointerException,集合里取出的 Integer 记得判空再拆。
Math 全静态方法:abs、max/min、pow、sqrt、round(四舍五入,注意返回 long)、Math.PI。Random 伪随机:
Random r = new Random(); // 可传种子,同种子同序列(可复现实验)
int dice = r.nextInt(6) + 1; // [0,6)+1 = 1~6,注意 nextInt(bound) 不含 bound
double prob = r.nextDouble(); // [0.0, 1.0)
「nextInt(6) + 1 忘 +1 得到 0~5」是新手高频事故;需要安全随机(密码学)用 SecureRandom,普通业务 Random 足够。
老 API(Date、Calendar、SimpleDateFormat)还在无数老代码里,新代码请直接用 JDK 8 的 java.time,它不可变、线程安全、API 说话像人话:
LocalDate today = LocalDate.now(); // 2026-10-24
LocalDate payday = today.plusDays(7); // 不可变:plus 返回新对象,同 String
LocalDateTime dt = LocalDateTime.of(2026, 10, 24, 21, 30);
Duration gap = Duration.between(start, end);
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
String text = dt.format(f); // 格式化
LocalDateTime back = LocalDateTime.parse(text, f); // 解析
旧 SimpleDateFormat 有个著名的坑(它不是线程安全的,多线程共享实例会算出荒唐日期),java.time 的 DateTimeFormatter 线程安全,这条也是老代码迁移的头号理由。月份从 1 数(Calendar 的月份从 0 数,历史事故之源)。
. 切分要写 split("\\.")(点在正则里是任意字符),按 | 切要 split("\\|"),直接传会得到诡异的空数组。s += x 和 StringBuilder,计时对比(预期百倍级差距),用 JConsole 或任务管理器顺带观察 GC 行为。Integer.valueOf(127) == Integer.valueOf(127) 和 128 版的结果;再用 equals 修复。写一句缓存范围注释贴在自己代码模板里。