Matcher.find()用于子串匹配并提取内容,Matcher.matches()要求整串匹配仅用于校验;group(0)是全匹配字符串,捕获组编号按左括号顺序递增;Pattern应复用,Matcher需每次新建或谨慎reset。

Matcher.find() 和 Matcher.matches() 到底该用哪个
区别不在“找不找得到”,而在“匹配范围”——matches() 要求整个输入字符串完全符合正则,find() 只要子串匹配就返回 true。比如用 "\d+" 去匹配 "abc123def":matches() 返回 false,find() 返回 true。
常见错误是想提取数字却用了 matches(),结果永远进不了 if 分支。实际提取分组时,99% 的场景都该先调 find()(或循环调用),再取 group。
- 想判断字符串是否「整体合规」(如邮箱格式校验)→ 用
matches() - 想从文本中「找出并提取内容」(如日志里抽 IP、URL)→ 必须用
find() - 用
matches()后直接调group(1)会抛IllegalStateException,因为没成功匹配捕获组
分组编号怎么算?为什么 group(0) 总是整个匹配
group(0) 是规范定义:它永远代表整个匹配到的字符串,不是第一个括号。真正第一个捕获组是 group(1),编号严格按左括号出现顺序递增,不管有没有命名、嵌套或多选分支。
容易踩的坑是正则里用了非捕获组 (?:...) 或条件分支 (a|b) —— 它们占位置但不产生 group 编号。例如 "(\d+)-(?:\w+)-(\d+)" 中,只有两个捕获组:group(1) 和 group(2),中间的 \w+ 不可取。
- 写完正则后,数一下
(的个数(排除(?:、(?=等),就是最大可用 group 编号 - 调试时别只打
group(1),先确认groupCount()返回几,再决定循环取到哪 - 如果正则含嵌套括号,比如
"((\d+)-(\d+))",group(1)是完整"123-456",group(2)是"123",group(3)是"456"
Pattern.compile() 能不能复用?性能差多少
能,而且必须复用。Pattern.compile() 是开销大户,内部要编译 NFA、构建状态机;而 Matcher 是轻量对象,每次调 matcher(input) 都很快。
实测:对同一正则反复 compile 10 万次,比复用一个 Pattern 对象慢 3–5 倍(JDK 17,Mac M1)。更严重的是 GC 压力——每个 Pattern 实例带一堆内部数组和缓存。
- 把常用正则提成
static final Pattern字段,比如private static final Pattern IP_PATTERN = Pattern.compile("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"); - 别在方法里写
Pattern.compile("...").matcher(str).find()这种一行流,除非正则是动态拼的(此时要考虑缓存 Map) - 注意线程安全:
Pattern是线程安全的,Matcher不是——每个线程必须用自己的Matcher实例(pattern.matcher(str)每次都新建)
Matcher.reset() 和重新 new Matcher 哪个更合适
绝大多数时候,直接 pattern.matcher(newString) 更清晰、更安全。reset() 看似省对象,但容易漏掉状态清理:它不会重置 region、hitEnd、requireEnd 等内部标记,尤其在前一次匹配失败或部分匹配后,行为可能出人意料。
典型问题:用 matcher.reset("abc") 后调 find(),结果还是沿用上一次的 region 范围,导致匹配不到。而 pattern.matcher("abc") 是干净的全新实例。
- 仅当明确需要保留 region 设置(比如多次扫描同一长文本的不同切片)才用
reset() - 用
reset()后,建议手动调usePattern(pattern)再确认一遍,避免旧 pattern 残留 - 别为了“省一个对象”牺牲可读性和稳定性——JVM 对短命对象的分配/回收早优化得差不多了
分组提取真正的难点不在语法,而在于匹配边界和 group 编号的隐式依赖。写完正则后,一定拿几个典型输入手跑一遍 group(0) 到 group(n),比看文档管用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











