第9章:字符串与常用工具类
讲完 OOP,是时候系统地过一遍 Java 标准库里那些你每天都在用的工具了。
📌 代码说明:标注"可复制运行"的代码块包含完整的
main方法。没标注的是概念示意片段。
一个故事开头
你写了一个奶茶店收银程序。顾客说:"我要三杯波波奶茶。"
你怎么把这句人话翻译成程序能理解的?你首先得分割字符串("三"、"波波奶茶"),把"三" 转换成数字 3,然后拼接成订单信息。
字符串处理、数字转换、时间记录——这些操作你写每个程序都会遇到的东西,Java 都已经给你准备好了。这一章就是带你把它们过一遍。
1. String——字符串
字符串是不可变的
这是 String 最重要的特性。String 对象一旦创建就不能修改。
String s = "Hello";
s.toUpperCase();
System.out.println(s); // 还是 "Hello"——s 没变!toUpperCase() 返回了一个新字符串,原来的 s 没变过。
String s = "Hello";
s = s + " World"; // 不是在原字符串上加——是创建了新字符串 "Hello World"
s = s.substring(0, 5); // 又创建了新字符串 "Hello"每次"修改"字符串都在创建新对象。频繁修改时性能差——这时用 StringBuilder(后面会讲)。
常用方法
String s = " Hello, World! ";
System.out.println(s.length()); // 17
System.out.println(s.trim()); // "Hello, World!"(去首尾空格)
System.out.println(s.strip()); // Java 11+,支持 Unicode 空格
System.out.println(s.toUpperCase()); // " HELLO, WORLD! "
System.out.println(s.contains("World")); // true
System.out.println(s.startsWith(" He")); // true
System.out.println(s.isEmpty()); // false
System.out.println(s.indexOf("World")); // 8(World 从索引8开始)
System.out.println(s.indexOf("xyz")); // -1(没找到)
System.out.println(s.charAt(2)); // 'H'(索引2的字符)
System.out.println(s.substring(2, 7)); // "Hello"(从2到6,不包含7)
System.out.println(s.replace("World", "Java")); // " Hello, Java! "💥 拆了它:== vs equals()
String a = "Hello";
String b = "Hello";
String c = new String("Hello");
System.out.println(a == b); // true(编译器优化:指向同一个对象)
System.out.println(a == c); // false!一个在常量池,一个在堆
System.out.println(a.equals(c)); // true(equals 比较内容)比较字符串内容永远用 equals(),不要用 ==。 == 比较的是引用(地址),不是内容。
StringBuilder——频繁拼接时用
// ❌ 大量拼接用 + 号——每次创建新对象,性能差
String s = "";
for (int i = 0; i < 1000; i++) {
s += i; // 创建了 1000 个临时 String 对象
}
// ✅ 用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 同一个对象,不创建新字符串
}
String result = sb.toString();2. Math 和 Random
// Math
Math.abs(-5); // 5(绝对值)
Math.max(10, 20); // 20
Math.min(10, 20); // 10
Math.round(3.6); // 4(四舍五入)
Math.floor(3.6); // 3.0(向下取整)
Math.ceil(3.2); // 4.0(向上取整)
Math.pow(2, 10); // 1024.0(2的10次方)
Math.sqrt(16); // 4.0(平方根)
// Random
import java.util.Random;
Random rand = new Random();
rand.nextInt(10); // 0~9 的随机整数
rand.nextInt(10) + 1; // 1~10 的随机整数
rand.nextDouble(); // 0.0~1.0 的随机小数3. 包装类与自动装箱
int、double、boolean 这些基本类型不是对象——但有时候你需要把它们当成对象(比如放进集合)。Java 给每个基本类型配了一个"包装类"。
| 基本类型 | 包装类 |
|---|---|
int | Integer |
double | Double |
boolean | Boolean |
char | Character |
long | Long |
// 手动装箱
Integer obj = Integer.valueOf(42);
// 手动拆箱
int val = obj.intValue();
// 自动装箱(Java 5+)——Java 自动帮你转
Integer obj2 = 42; // 自动装箱:int → Integer
int val2 = obj2; // 自动拆箱:Integer → int字符串转数字
int num = Integer.parseInt("123"); // "123" → 123
double d = Double.parseDouble("3.14"); // "3.14" → 3.14
boolean b = Boolean.parseBoolean("true"); // "true" → true💥 拆了它:解析失败的后果
int num = Integer.parseInt("奶茶"); // NumberFormatException!解析失败 Java 报异常——所以实际应用中要包 try-catch(第11章会讲)。
4. 枚举——有限个选项
一年有 12 个月,一周有 7 天,奶茶店杯型有大/中/小——这些"只能从几个固定值里选"的东西,用枚举(enum)。
enum Size {
SMALL, MEDIUM, LARGE
}
enum OrderStatus {
PENDING, PROCESSING, COMPLETED, CANCELLED
}Size size = Size.MEDIUM;
// switch 枚举
switch (size) {
case SMALL -> System.out.println("小杯 — 8元");
case MEDIUM -> System.out.println("中杯 — 12元");
case LARGE -> System.out.println("大杯 — 15元");
}
// 枚举自带方法
System.out.println(Size.values().length); // 3(枚举值的个数)
System.out.println(Size.valueOf("SMALL")); // SMALL枚举比用 int 常量好在哪里? 你不可能传一个 int 值进去,说"杯型是 4"——但用枚举,编译器会检查。
5. 时间和日期
Java 8 之前的时间 API(Date、Calendar)设计得很烂。Java 8 引入了全新的 java.time 包——干净、好用。
import java.time.*;
// 当前时间
LocalDate today = LocalDate.now(); // 2026-06-22
LocalTime now = LocalTime.now(); // 12:08:00.123
LocalDateTime nowFull = LocalDateTime.now(); // 2026-06-22T12:08:00.123
// 创建具体日期
LocalDate birthday = LocalDate.of(2000, 1, 1); // 2000-01-01
// 加减
LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate lastYear = today.minusYears(1);
// 比较
today.isAfter(birthday); // true
today.isBefore(birthday); // false
today.isEqual(birthday); // false
// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
System.out.println(today.format(fmt)); // 2026年06月22日6. Record——纯数据载体
如果你有一个类,它的全部作用就是"装着几个值",写起来很啰嗦——要写构造方法、getter、equals、toString...
// 传统写法
class Point {
private int x;
private int y;
Point(int x, int y) { this.x = x; this.y = y; }
public int x() { return x; }
public int y() { return y; }
// equals/hashCode/toString...
}Java 16+ 引入的 Record 帮你把这一切自动化:
// Record——一行搞定
record Point(int x, int y) { }
// 直接用
Point p = new Point(3, 5);
System.out.println(p.x()); // 3(自动生成 getter,叫 x() 不是 getX())
System.out.println(p.toString());// Point[x=3, y=5]
System.out.println(p.equals(new Point(3, 5))); // true(自动生成 equals)用在奶茶店
record Drink(String name, double price) {}
Drink d1 = new Drink("波波奶茶", 15.0);
System.out.println(d1.name()); // 波波奶茶完整例子
import java.time.*;
enum OrderStatus { PENDING, PROCESSING, COMPLETED, CANCELLED }
record OrderItem(String drink, int quantity, double unitPrice) {
public double total() { return quantity * unitPrice; }
}
public class OrderDemo {
public static void main(String[] args) {
// 枚举
OrderStatus status = OrderStatus.PENDING;
System.out.println("订单状态: " + status);
// Record
OrderItem item1 = new OrderItem("波波奶茶", 2, 15.0);
OrderItem item2 = new OrderItem("柠檬茶", 1, 12.0);
System.out.println(item1.drink() + " × " + item1.quantity() + " = " + item1.total() + "元");
// 时间
LocalDateTime orderTime = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
System.out.println("下单时间: " + orderTime.format(fmt));
// 字符串处理
String receipt = "订单: " + item1.drink() + "等" + (item1.quantity() + item2.quantity()) + "杯";
System.out.println(receipt);
System.out.println("包含'奶茶'? " + receipt.contains("奶茶"));
}
}本章小结
- String——不可变,比较内容用
equals(),大量拼接用StringBuilder - Math/Random——数学运算、随机数生成
- 包装类——
int→Integer,字符串转数字用parseXxx() - 枚举——有限个选项,比
int常量安全 - java.time——
LocalDate、LocalTime、LocalDateTime,Java 8+ 时间 API - Record——Java 16+,一行搞定纯数据类
✅ 验收标准
完成本章后,你应该能:
- [ ] 解释 String 不可变性的意义
- [ ] 区分
equals()和==的字符串比较 - [ ] 用 StringBuilder 做大量字符串拼接
- [ ] 用 Math 和 Random 做数学计算和随机数
- [ ] 声明和使用枚举
- [ ] 用 java.time API 处理日期时间
📌 常见卡点
- 用 `==` 比较字符串可能偶然正确但不可靠——总用 `equals()`
- 字符串拼接太多用 `+`——循环里用 StringBuilder
- 枚举在 switch 里不用写 break(Java 14+ arrow 语法)
- 日期格式化注意月份是 1-12(新 API)还是 0-11(旧 Date)
🔜 现在不需要理解
- 正则表达式——单独章节
- 国际化(Locale)——项目用到再学
- DecimalFormat 数字格式化——用到时查
- ZoneId 时区处理——高级场景
🧪 练习
1. 字符串方法:声明 String s = "Java 编程很有趣",用合适的方法判断它是否包含"编程",提取"编程"这个词,把它替换成"开发"。
2. 枚举:定义一个 Season 枚举(SPRING, SUMMER, AUTUMN, WINTER),写一个 switch 根据季节输出对应的中文名。
3. 时间:用 LocalDate 输出今天的日期、三天后的日期、以及今天是否在你的生日之后。(提示:LocalDate.of(2000, 1, 1))