Java正则表达式的基本语法与使用

Java正则表达式通过java.util.regex包实现,需注意双反斜杠转义、replaceAll与replace区别、matches与find语义差异,以及预编译Pattern提升性能。

Java 正则表达式不是独立语言,而是通过 java.util.regex 包(主要是 PatternMatcher)实现的字符串匹配工具。它和 Perl、JavaScript 的正则语法高度兼容,但有几处关键差异必须注意,否则会匹配失败或抛出 PatternSyntaxException

Java 中写正则时,反斜杠必须写成双反斜杠

这是最常

踩的坑:Java 字符串本身会转义一次 \,正则引擎又要再转义一次。比如想匹配一个数字字符 \d,在 Java 字符串里得写成 "\\d";想匹配一个反斜杠字面量 \,得写成 "\\\\"

常见错误现象:

  • "\d+" → 编译报错:Invalid escape sequence
  • "\bword" → 实际匹配的是退格符 + "word",不是单词边界

实操建议:

  • 用原始字符串思维去想正则逻辑,再把每个 \ 替换成 \\
  • IDE 通常会高亮提示字符串转义问题,别忽略黄色波浪线
  • 对复杂正则,优先用 Pattern.compile() 配合 Pattern.LITERALPattern.CASE_INSENSITIVE 标志,减少转义负担

String.replaceAll() 和 Pattern.matcher().replaceAll() 的行为差异

String.replaceAll() 内部就是调用 Pattern.compile(regex).matcher(this).replaceAll(replacement),但它要求第一个参数是合法正则——哪怕你只想替换固定字符串,比如 "a.b",其中的 . 也会被当作元字符匹配任意字符。

使用场景:

  • 需要正则替换(如 "\\d+".replaceAll("\\d+", "#"))→ 用 replaceAll()
  • 只想做字面量替换(如把所有 "a.b" 换成 "x")→ 改用 String.replace("a.b", "x")(不走正则)或 Pattern.quote("a.b") 包装

性能影响:每次调用 String.replaceAll() 都会重新编译正则;高频调用建议缓存 Pattern 实例。

Matcher.find() 与 Matcher.matches() 完全不同

matches() 要求整个输入字符串**完全匹配**正则,而 find() 只要在字符串中找到**任意子串匹配**就返回 true。新手常误以为 "abc123".matches("\\d+") 会返回 true,实际是 false,因为整个字符串不全是数字。

典型错误:

  • 校验邮箱用 str.matches("@") → 应该用 str.matches("^.+@.+$") 或更严谨的模式
  • 提取数字写成 matcher.matches() → 结果永远 false,应改用 find() + group()

实操建议:

  • 校验类场景(如密码强度、手机号格式)用 matches()
  • 提取、查找、分组类场景(如解析日志、提取 URL 参数)必须用 find()
  • 注意 find() 是游标式遍历,多次调用会移动位置;重用前需 matcher.reset()

预编译 Pattern 提升性能与可读性

重复使用同一正则时,每次都调用 Pattern.compile() 是浪费。尤其在循环、Servlet 请求处理等高频路径中,应将 Pattern 声明为 static final

public class RegexUtils {
    private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");

    public static boolean isValidEmail(String email) {
        return EMAIL_PATTERN.matcher(email).matches();
    }
}

好处:

  • 避免重复编译开销(JIT 优化有限,编译本身有成本)
  • 正则集中管理,便于复用和单元测试
  • 可配合 Pattern.COMMENTS 写带注释的多行正则(需手动去除空白和换行)

容易被忽略的地方:正则中的 Unicode 字符类(如 \\p{L})在不同 JDK 版本下行为可能微调,生产环境建议锁定 JDK 小版本并覆盖测试。