☕ 第六章 Java API
📝 一句话概括

正则表达式 = 字符串的筛选模板——把「一个字符一个字符地 if」压缩成一行规则,校验、切割、替换三种活一肩挑。

6.8 正则表达式

1. 为什么需要正则?

5.5 给 Person.setAge 做过安检:age >= 0 && age <= 150。现在换个活——校验手机号:

// 老写法:逐条 if
if (s.length() == 11
        && s.charAt(0) == '1'
        && s.charAt(1) >= '3' && s.charAt(1) <= '9'
        && 全是数字……) { ... }

正则写法:

boolean ok = s.matches("1[3-9]\\d{9}");   // 一行顶十行

正则的本质:用一个「模板字符串」描述一类字符串的长相。模板由四类零件拼成。

2. 四大零件(语法速查)

类别 写法 含义 示例
① 精确字符 abc @ - 原样逐位匹配 abc 配 "abc"
② 字符集合 [abc] [a-z] [^0-9] 方括号里选一个;^ 开头取反 1[3-9] 配 13~19
③ 量词 * + ? {n} {n,} {n,m} 前一个东西重复几次 \d{9} 九位数字
④ 预定义 \d \w \s . 数字 / 单词字符 / 空白 / 任意一个 \d+ 一串数字

量词对照:

量词 次数 记法
* 0 次或多次 随便
+ 1 次或多次 至少来一个
? 0 次或 1 次 有没有都行
{n} 恰好 n 次 定死的
{n,m} n 到 m 次 弹性区间
⚠️ 头号坑:转义要过「两层皮」

正则里"数字"写 \d,但 Java 字符串里 \ 本身是转义符——所以源代码必须写 "\\d"(两个斜杠到正则手里剩一个)。 同理:正则匹配「点」写 \.,源代码写 "\\."(点不转义会变成"任意字符")。 口诀:Java 字符串里的正则,斜杠一律翻倍。

3. Java 三种使用姿势(String 自带,不用 import)

姿势一:matches —— 整串校验

String phone = "13812345678";
System.out.println(phone.matches("1[3-9]\\d{9}"));   // true

String bad = "12812345678";   // 第二位 2 不在 [3-9]
System.out.println(bad.matches("1[3-9]\\d{9}"));     // false

注意:matches 要求整串完全匹配——多一位少一位、位置不对都是 false,不是"包含就匹配"。

姿势二:split —— 正则切割

6.2 用 split 只能按单一字符切;分隔符其实是个正则,能力瞬间升级:

"a,b;c d".split("[,; ]")       // [a, b, c, d] —— 三种分隔符一次切
"a    b  c".split("\\s+")      // [a, b, c] —— 连续空白一刀切干净
"a,b".split(",")               // 老用法照样行

姿势三:replaceAll —— 正则替换

"138 1234 5678".replaceAll("\\s", "")       // 13812345678 —— 去掉所有空格
"abc123def456".replaceAll("\\d", "*")       // abc***def*** —— 所有数字打码
"我a爱bC".replaceAll("[a-zA-Z]", "")         // 我爱 —— 按类删除

6.2 的 replace 只能换死字符串;replaceAll 换的是一类。

4. 综合实战:三个经典模板

public class Demo08 {
    public static void main(String[] args) {
        // ① 手机号校验(5.5 的 setPhone 安检直接升级)
        System.out.println("13912345678".matches("1[3-9]\\d{9}"));      // true

        // ② 邮箱校验(简化版)
        System.out.println("[email protected]".matches("\\w+@\\w+\\.[a-z]{2,}")); // true

        // ③ 成绩单拆分:split + parseInt(6.2 毕业题的正则版)
        String line = "语文=95,数学=88,英语=92";
        for (String subject : line.split(",")) {
            String[] kv = subject.split("=");
            System.out.println(kv[0] + ":" + kv[1]);
        }
    }
}
💡 邮箱模板里的 `\.`

\w+@\w+\.[a-z]{2,} 中的点必须转义——不转义的 . 是"任意字符",a@bXcom 也能混过去,校验就白做了。

5. 常见误区

⚠️ 误区 1:斜杠只写一个(`"\d"`)

编译直接报错——Java 字符串里 \d 不是合法转义(Invalid escape sequence)。源代码里斜杠一律翻倍。

⚠️ 误区 2:以为 matches 是"包含匹配"

"abc13812345678".matches("1[3-9]\\d{9}") 是 false——matches 从头到尾整串比对。想"找一段"要用 Pattern/Matcher 的 find(),后面再学。

⚠️ 误区 3:模板写错,运行时才炸

正则语法错误(比如 "[abc" 少半边括号)编译不拦,运行时抛 PatternSyntaxException——运行时异常,5.2 收容所那派。模板复杂时先在在线正则工具里试,再搬进代码。

⚠️ 误区 4:点不转义

. 在正则里是"任意字符"。校验类模板里遇到字面的点(域名、文件扩展名)必须 \\.。

⚠️ 误区 5:拿正则硬套所有问题

正则强在格式校验,不管业务规则:能验"像不像手机号",验不了"这号注册过没"。格式归正则、业务归 if,别混。

6. 动手试一试(30 秒实验)

新建 Demo08:

实验 1:"a1b2c3".matches("[a-z]\\d") 结果是 false——因为是 6 个字符不是 3 个;改成 "[a-z]\\d[a-z]\\d[a-z]\\d" 试试,再改 ([a-z]\\d){3} 一遍过——体会量词+分组的威力。

实验 2:"a,b;c d".split("[,; ]") 和 .split("\\s+") 各打一遍,数数组长度——体会字符集合与 \s+ 的区别。

实验 3:"abc123".replaceAll(".", "*") ——参数只写一个 .(Java 字符串里点不用转义),结果整串变 ******——亲眼见正则里"点=任意字符";再改成 "\\." 试 [email protected]".replaceAll("\\.", "#"),只有真正的点被替换——对照体会转义。

实验 4:写 "[abc" 作为 matches 参数,看运行时抛 PatternSyntaxException——确认它编译不拦。

7. 自测一下

❓ Q1:`\\d`、`\\w`、`\\s` 分别代表什么?为什么源代码里要写两个斜杠?

数字、单词字符(字母数字下划线)、空白。正则本身是 \d,Java 字符串里 \ 是转义符,必须写 \\ 才能传给正则引擎一个 \——两层皮。

❓ Q2:`matches("1[3-9]\\d{9}")` 为什么能同时保证"11 位、1 开头、第二位 3-9、全是数字"?少一位会怎样?

模板逐位咬合:1(第一位)+ [3-9](第二位)+ \d{9}(后九位)= 11 位全定义。少一位则整串匹配失败返回 false——matches 是全串校验。

❓ Q3:`"a b,c".split("\\s+|,")` 的结果?这个模板用了哪两类零件?

["a", "b", "c"]——\s+(预定义+量词)按连续空白切,|(或)把逗号并进来,字符集合也行:"[,\\s]+"。

❓ Q4:邮箱模板里 `\.` 不转义会发生什么?举一个能混过校验的错误邮箱。

不转义的 . 配任意字符,a@bxc om 变形串(如 a@bx1com)也能通过——校验形同虚设。凡字面的点必须 \\.。

❓ Q5(毕业题):把 `"138 1234 5678"` 变成脱敏形式 `138****5678`(中间四位打码),写出思路。

两步:先 replaceAll("\\s", "") 去空格得 13812345678;再 replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")——分组保留前 3 后 4,中间四位换星号。(分组 $1/$2 混脸熟即可,后面细学)

8. 下一步学什么


相关笔记