Java 正则表达式基础 📅 2026/6/26 19:33:32 第一部分Java 正则表达式核心概念与底层原理在进入实战场景前我们必须先厘清正则表达式的底层基本逻辑以及 Java 平台的实现机制 —— 这是后续编写高性能匹配模式、定位和解决性能问题的基础前提。1.1 正则表达式的本质正则表达式Regular Expression常简写为 regex是一种用于匹配字符串模式的领域专用语言 —— 它用一种结构化的语法规则来描述一类符合特定规则的字符串样本而不是用代码逻辑逐个字符进行判断。在业务开发中它是文本处理场景的终极解决方案核心应用场景可以归纳为四类验证检查输入字符串的格式是否符合业务规则 —— 比如校验用户输入的手机号、邮箱地址、身份证号的格式是否合法提取从指定文本中批量获取符合规则的子串 —— 比如从用户提交的文本中提取所有符合规则的订单号或者从 HTML 或日志文件中提取特定业务节点的关键信息替换将匹配到的子串统一替换为指定的新内容 —— 比如隐藏用户敏感信息、过滤文本中的敏感词、或者将文本中的 HTML 标签统一清除分割根据复杂的规则分隔符将字符串切分为多个子串 —— 比如将前端传入的多分隔符数据统一切分为标准的字符串数组。与其他文本处理方式相比正则表达式的核心优势在于「用简短的语法逻辑表达一套完整的、灵活的字符串匹配规则」—— 对于业务开发中绝大多数文本处理需求都可以通过正则表达式的标准匹配逻辑来解决而无需编写冗余的业务级代码。第二部分Java 正则 API 使用指南 —— 从基础到高级理解了底层原理我们再来看 Java 正则 API 的具体使用方式。Java 中的正则表达式使用方式可以分为「便捷方式」和「标准方式」两类 —— 不同的方式适用于不同的业务场景。2.1 两类使用方式2.1.1 便捷方式利用 String 类的内置方法对于简单、低频的正则匹配场景JDK 提供了一种极简的使用方式 —— 直接调用String类中内置的三个正则相关方法就可以完成匹配操作而无需手动创建Pattern和Matcher实例。这三个方法的核心功能和适用场景如下String.matches(String regex)校验当前完整字符串是否匹配传入的正则表达式规则String.replaceAll(String regex, String replacement)将当前字符串中所有匹配正则表达式规则的子串替换为指定的新内容String.replaceFirst(String regex, String replacement)只替换当前字符串中第一个匹配正则表达式规则的子串String.split(String regex)根据传入的正则表达式规则作为分隔符将当前字符串分割为一个字符串数组。这类方法的核心优势是编码简单只需一行代码即可完成匹配操作适合逻辑简单、执行频率较低的业务场景。但很多开发者不知道的是这类便捷方法底层实际上仍会去编译Pattern对象—— 每次调用String类的正则方法时都会隐式调用Pattern.compile()编译一次正则表达式生成一个新的Pattern实例。如果在循环中、或者高频业务场景中频繁调用这类方法就会导致大量重复的编译操作急剧增加 CPU 资源消耗引发严重的性能问题。2.1.2 标准方式Pattern 与 Matcher 组合对于高频、复杂的正则匹配场景必须使用标准的 API 调用方式 —— 手动处理正则表达式的编译和匹配逻辑。这一方式的核心流程可以拆解为三个明确的步骤编译正则表达式使用Pattern.compile()方法将字符串形式的正则表达式编译为一个Pattern实例 —— 这一过程只需要执行一次创建匹配器调用Pattern.matcher()方法传入需要匹配的目标字符串获取一个Matcher实例执行匹配逻辑调用Matcher实例的相关方法执行具体的匹配操作 —— 匹配成功后还可以通过Matcher的group()方法获取匹配到的具体结果。这一标准方式的核心优势是可以复用Pattern实例 —— 这是提升正则表达式性能的最核心手段下一章我们会深入讲解这一优化逻辑。2.2 Matcher 类的核心匹配方法Matcher类是执行匹配操作的核心入口它提供了三个功能差异极大的匹配方法分别对应不同的业务场景。很多开发者会混淆这三个方法的使用逻辑导致匹配结果不符合业务预期 —— 在使用时必须根据实际业务场景的需求选择正确的方法执行匹配逻辑matches()尝试对整个目标字符串进行完整匹配 —— 只有当整个字符串完全匹配正则表达式的规则时才会返回true。这一方法适用于「校验整个字符串的格式是否符合业务规则」的场景比如校验用户输入的手机号、邮箱格式是否合法lookingAt()尝试从目标字符串的开头进行匹配 —— 只有当字符串的前缀部分匹配正则表达式规则时才会返回true。这一方法适用于「校验字符串的前缀是否符合特定规则」的场景比如校验用户上传的文件名是否以指定的业务前缀开头find()扫描整个目标字符串查找所有匹配规则的子串 —— 这一方法适用于「提取字符串中所有匹配规则的子串」的场景比如从一段文本中提取所有的订单号、手机号或邮箱地址。需要特别注意的是这三个方法都会重置Matcher实例的内部匹配指针状态—— 在同一组匹配规则下多个方法之间会相互影响导致匹配结果不符合预期。在实际业务场景中建议在创建Matcher实例后只使用其中一种方法执行匹配逻辑避免混合调用引发意想不到的匹配异常。2.3 实战基础示例校验、提取、替换、分割接下来我们通过四个最常见的业务场景示例来演示标准 API 的具体使用方式。场景一数据校验完全匹配这是后端开发中最常见的业务场景 —— 校验用户输入的手机号格式是否合法。由于校验逻辑会被高频调用我们需要使用标准的 API 调用方式预先编译好Pattern对象并复用它import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexValidationExample { // 将正则表达式预编译为静态常量类加载时只编译一次后续可以全局复用 private static final Pattern PHONE_PATTERN Pattern.compile(^1[3-9]\\d{9}$); /** * 校验手机号格式是否合法 * param phone 待校验的手机号字符串 * return 校验结果true表示格式合法false表示格式不合法 */ public static boolean isValidPhoneNumber(String phone) { // 对空值或长度不符合预期的字符串进行快速校验 if (phone null || phone.length() ! 11) { return false; } // 创建匹配器执行匹配操作 Matcher matcher PHONE_PATTERN.matcher(phone); // 这里使用matches()方法因为需要对整个手机号进行完整匹配 return matcher.matches(); }}在这个示例中我们将Pattern对象定义为静态常量 —— 这意味着在类加载时正则表达式只会被编译一次后续的每次校验逻辑都会直接复用这一编译后的实例避免了重复编译带来的性能损耗。场景二数据提取查找子串从一段普通文本中提取出所有符合规则的手机号或邮箱地址 —— 这是日志分析、用户输入数据清洗场景下的常见需求。实现这一需求的核心是正确使用Matcher.find()方法import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexExtractionExample { // 预编译手机号匹配规则 private static final Pattern PHONE_PATTERN Pattern.compile(\\b1[3-9]\\d{9}\\b); // 预编译邮箱匹配规则 private static final Pattern EMAIL_PATTERN Pattern.compile( \\b[A-Za-z0-9._%-][A-Za-z0-9.-]\\.[A-Za-z]{2,}\\b ); /** * 从输入文本中提取所有匹配规则的手机号 * * param text 待处理的目标文本 * return 匹配到的手机号列表 */ public static ListString extractPhoneNumbers(String text) { ListString phones new ArrayList(); Matcher matcher PHONE_PATTERN.matcher(text); // 循环调用find方法扫描整个文本查找所有匹配的子串 while (matcher.find()) { // 使用group()方法获取当前匹配到的子串 phones.add(matcher.group()); } return phones; } /** * 从输入文本中提取所有匹配规则的邮箱地址 * * param text 待处理的目标文本 * return 匹配到的邮箱地址列表 */ public static ListString extractEmails(String text) { ListString emails new ArrayList(); Matcher matcher EMAIL_PATTERN.matcher(text); while (matcher.find()) { emails.add(matcher.group()); } return emails; } }在这个示例中我们使用\b元字符来匹配单词的边界确保不会提取到不符合规则的中间数字串 —— 这是数据提取场景中常用的匹配规则优化手段避免匹配到业务不需要的子串导致提取结果出现逻辑错误。场景三敏感词替换在用户输入的内容中将所有匹配规则的敏感词统一替换为固定的掩码字符 —— 这是内容审核、用户数据脱敏场景下的常见需求。实现这一需求的核心是正确使用Matcher.replaceAll()方法import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexReplacementExample { /** * 替换敏感词将输入文本中的所有敏感词替换为固定长度的掩码字符 * * param text 待处理的目标文本 * param sensitiveWords 要替换的敏感词数组 * return 替换后的脱敏文本 */ public static String filterSensitiveWords(String text, String[] sensitiveWords) { // 构造敏感词的正则匹配规则将所有敏感词用 | 连接匹配其中任意一个 String regex String.join(|, sensitiveWords); // 编译正则表达式使用 CASE_INSENSITIVE 标志忽略大小写进行匹配 Pattern pattern Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher matcher pattern.matcher(text); // 使用 replaceAll 方法将所有匹配到的敏感词替换为 *** return matcher.replaceAll(***); } public static void main(String[] args) { String content 这个商品的质量很差垃圾至极我实在是太失望了; String[] sensitiveWords {垃圾, 差, 失望}; String filteredContent filterSensitiveWords(content, sensitiveWords); System.out.println(脱敏后的文本 filteredContent); // 输出脱敏后的文本这个商品的质量很******至极我实在是太***了 } }需要特别注意的是在这个场景中我们使用了Pattern.CASE_INSENSITIVE匹配标志 —— 这会让匹配规则忽略大小写从而匹配到不同大小写的敏感词。此外敏感词数组的元素需要按较长的词优先排序 —— 否则部分短的敏感词会先被匹配替换导致长敏感词无法被正确匹配替换。场景四复杂字符串分割前端传入的字符串使用了多种分隔符 —— 比如逗号、分号、竖线需要将其统一分割为标准的字符串数组。这是数据解析、多标签处理场景下的常见需求。实现这一需求的核心是在split()方法中传入匹配所有分隔符的正则表达式规则import java.util.Arrays; import java.util.regex.Pattern; public class RegexSplittingExample { // 预编译匹配多种分隔符的正则表达式规则匹配逗号、分号、竖线以及其周围的空白字符 private static final Pattern DELIMITER_PATTERN Pattern.compile(\\s*[,;|]\\s*); public static void main(String[] args) { String data apple,banana;orange|grape ; // 使用正则表达式规则分割字符串 String[] fruits DELIMITER_PATTERN.split(data); // 对结果进行过滤去除可能存在的空白字符串 fruits Arrays.stream(fruits) .map(String::trim) .filter(s - !s.isEmpty()) .toArray(String[]::new); System.out.println(Arrays.toString(fruits)); // 输出[apple, banana, orange, grape] } }在这个示例中我们使用\s*来匹配分隔符周围的所有空白字符 —— 这可以避免结果中出现多余的空格同时在分割完成后我们对结果数组进行了过滤去除可能存在的空白字符串 —— 这可以有效避免输入数据不规范导致的分割结果异常。