1. 概述

在 Java 中处理正则表达式时,我们通常需要在一个字符序列中查找特定的 Pattern。为此,Java 正则表达式 API 提供了 Matcher 类,用于将正则表达式与目标文本进行匹配。

核心要点:实际开发中,我们最常使用的 Matcher 方法就两个:

  • find()
  • matches()

本文通过几个简单但典型的例子,带你彻底搞清这两个方法的差异,避免踩坑。

2. find() 方法:查找任意位置的匹配

find() 的作用很简单:在目标字符串中查找是否存在与正则匹配的子串,只要有一处匹配就返回 true

⚠️ 关键特性:

  • 不要求整个字符串完全匹配
  • 可以多次调用,每次找到下一个匹配项(类似迭代器)
  • 常用于提取多个匹配结果

举个例子:我们要从字符串 "goodbye 2019 and welcome 2020" 中提取所有四位数。

使用正则 \d\d\d\d

@Test
public void whenFindFourDigitWorks_thenCorrect() {
    Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
    Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");

    assertTrue(m.find()); // 找到第一个:2019
    assertEquals(8, m.start());
    assertEquals("2019", m.group());
    assertEquals(12, m.end());
    
    assertTrue(m.find()); // 找到第二个:2020
    assertEquals(25, m.start());
    assertEquals("2020", m.group());
    assertEquals(29, m.end());
    
    assertFalse(m.find()); // 没有了,返回 false
}

📌 输出说明:

  • start():匹配子串的起始索引(含)
  • end():匹配子串结束后的下一个位置(不含)
  • group():实际匹配到的内容

✅ 两次 find() 调用分别捕获了 20192020,第三次返回 false,说明已遍历完毕。

3. find(int) 方法:从指定位置开始查找

find(int start)find() 的重载版本,允许你指定搜索的起始位置。

✅ 使用场景:跳过前面的内容,只关心某位置之后的匹配。

继续上面的例子,如果我们只想找索引 20 之后的四位数:

@Test
public void givenStartIndex_whenFindFourDigitWorks_thenCorrect() {
    Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
    Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");

    assertTrue(m.find(20)); // 从索引 20 开始找
    assertEquals(25, m.start());
    assertEquals("2020", m.group());
    assertEquals(29, m.end());  
}

结果只匹配到 2020,因为 2019 出现在索引 8,早于 20,被跳过了。

💡 提示:这个方法在解析日志、分段处理文本时特别有用,避免重复匹配。

4. matches() 方法:全字符串匹配

matches()find() 完全不同:它要求整个字符串必须完全匹配正则表达式

❌ 常见误区:以为 matches() 是“有没有匹配”,其实是“是不是完全匹配”。

看例子:

@Test
public void whenMatchFourDigitWorks_thenFail() {
    Pattern stringPattern = Pattern.compile("\\d\\d\\d\\d");
    Matcher m = stringPattern.matcher("goodbye 2019 and welcome 2020");
 
    assertFalse(m.matches()); // 返回 false
}

原因:\d\d\d\d 只能匹配 4 个数字,而整个字符串远不止这些字符。

但如果输入本身就是 2019,那就完全匹配了:

@Test
public void whenMatchFourDigitWorks_thenCorrect() {
    Pattern stringPattern = Pattern.compile("\\d\\d\\d\d");
    Matcher m = stringPattern.matcher("2019");
    
    assertTrue(m.matches()); // 成功
    assertEquals(0, m.start());
    assertEquals("2019", m.group());
    assertEquals(4, m.end());
}

✅ 注意:matches() 是幂等的,无论调用多少次,只要输入不变,结果都一样。而 find() 多次调用会移动指针,结果可能不同。

5. matcher() 与 Pattern.matches() 的区别

这里容易混淆,我们来澄清一下:

方法 类型 行为
pattern.matcher(input) 实例方法 返回 Matcher 对象,可多次操作(如 find()matches()
Pattern.matches(regex, input) 静态方法 编译正则 + 全字符串匹配,一步到位

简单粗暴理解:

  • matcher()“字符串里有没有这个模式?”
  • Pattern.matches()“字符串是不是这个模式?”

示例对比:

@Test
public void whenUsingMatcher_thenReturnTrue() {
    Pattern pattern = Pattern.compile("\\d\\d\\d\\d");
    Matcher matcher = pattern.matcher("goodbye 2019 and welcome 2020");

    assertTrue(matcher.find()); // ✅ 找得到 2019
}
@Test
public void whenUsingMatches_thenReturnFalse() {
    String REGEX = "\\d\\d\\d\\d";
    String STRING_INPUT = "goodbye 2019 and welcome 2020";

    assertFalse(Pattern.matches(REGEX, STRING_INPUT)); // ❌ 不是全数字
}

📌 实际建议:

  • 需要提取内容、多轮匹配 → 用 matcher() + find()
  • 只做一次校验(如手机号、邮箱格式)→ 可直接用 Pattern.matches()

6. 总结

方法 匹配范围 是否可迭代 典型用途
find() 任意子串 ✅ 可多次调用 提取多个匹配项
find(int) 从指定位置开始 跳过前段内容
matches() 整个字符串 ❌ 幂等 格式校验
Pattern.matches() 静态全匹配 简单一次性校验

✅ 核心口诀:

find 找得到,matches 全符合。

掌握这两者的区别,能让你在处理文本解析、日志提取、输入校验等场景时少走弯路。代码已上传至 GitHub 仓库:https://github.com/dev-example/java-regex-demo


原始标题:Difference Between Java Matcher find() and matches()