java.util.regex.Pattern 是 Java 中用于编译正则表达式核心类,它代表一个经过编译的模式,可以被用来创建 Matcher 对象,从而在字符串中进行匹配操作。

理解 Pattern 的工作流程是掌握 Java 正则表达式的关键:
- 编译: 将一个正则表达式字符串(如
"\d+")编译成一个Pattern对象,这个过程相对耗时,所以如果需要多次使用同一个正则表达式,最好只编译一次并复用Pattern对象。 - 匹配: 使用
Pattern对象创建一个Matcher对象。 - 操作: 通过
Matcher对象在目标字符串上进行查找、替换、分割等操作。
核心概念
Pattern 类
Pattern 类是一个不可变的(final)类,它包含了已编译的正则表达式。
- 如何创建? 不能使用
new关键字直接实例化,必须通过其静态工厂方法compile(String regex)来创建。// 编译正则表达式 \d+ (匹配一个或多个数字) Pattern pattern = Pattern.compile("\\d+");
Matcher 类
Matcher 类是对输入字符串执行匹配操作的引擎,它提供了各种查找方法。
- 如何创建? 通过
Pattern对象的matcher(CharSequence input)方法创建。String input = "我的电话是13812345678,座机是010-12345678。"; Matcher matcher = pattern.matcher(input);
常用方法
Pattern 类的常用方法
| 方法 | 描述 | 示例 |
|---|---|---|
static Pattern compile(String regex) |
编译给定的正则表达式字符串,返回一个 Pattern 对象。 |
Pattern p = Pattern.compile("\\d+"); |
static Pattern compile(String regex, int flags) |
编译正则表达式,并指定标志(如大小写不敏感等)。 | Pattern p = Pattern.compile("abc", Pattern.CASE_INSENSITIVE); |
String pattern() |
返回原始编译时的正则表达式字符串。 | p.pattern(); // 返回 "\\d+" |
String[] split(CharSequence input) |
根据模式分割输入字符串,返回字符串数组。 | p.split("a1b2c3"); // 返回 ["a", "b", "c"] |
String[] split(CharSequence input, int limit) |
根据模式分割输入字符串,limit 参数控制分割的次数。 |
p.split("a1b2c3", 2); // 返回 ["a", "b2c3"] |
Matcher 类的常用方法
| 方法 | 描述 | 示例 |
|---|---|---|
boolean find() |
尝试在字符串中查找下一个匹配的子序列。这是最常用的方法。 | while (m.find()) { ... } |
boolean find(int start) |
重置匹配器,并尝试从指定的索引位置开始查找。 | m.find(10); |
boolean matches() |
尝试将整个区域与模式匹配。要求整个字符串都必须匹配。 | Pattern p = Pattern.compile("\\d+"); p.matcher("123").matches(); // truep.matcher("a123").matches(); // false |
boolean lookingAt() |
尝试将区域开头与模式匹配。只要求字符串开头匹配即可。 | Pattern p = Pattern.compile("\\d+"); p.matcher("123abc").lookingAt(); // truep.matcher("abc123").lookingAt(); // false |
String group() |
返回当前匹配到的子串。 | m.group(); // 返回 "13812345678" |
int start() |
返回当前匹配子串的起始索引。 | m.start(); // 返回 4 |
int end() |
返回当前匹配子串的结束索引(exclusive,即下一个字符的索引)。 | m.end(); // 返回 16 |
int groupCount() |
返回匹配器模式中的捕获组数量。0 表示整个模式。 |
m.groupCount(); |
String group(int group) |
返回指定捕获组匹配到的子串。 | Pattern p = Pattern.compile("(\\d{3})(\\d{4})(\\d{4})");m.group(1); // 返回 "138" |
常用正则表达式元字符
这里列出一些最常用的元字符,它们是构建正则表达式的基础。

| 元字符 | 描述 | 示例 |
|---|---|---|
| 匹配除换行符以外的任意单个字符。 | a.c 匹配 "abc", "aac", "a c" |
|
^ |
匹配字符串开头的位置。 | ^abc 匹配 "abc123",不匹配 "123abc" |
| 匹配字符串结尾的位置。 | abc$ 匹配 "123abc",不匹配 "abc123" |
|
| 匹配前面的子表达式零次或多次。 | a* 匹配 "", "a", "aa", "aaa" |
|
| 匹配前面的子表达式一次或多次。 | a+ 匹配 "a", "aa", "aaa",不匹配 "" |
|
| 匹配前面的子表达式零次或一次。 | a? 匹配 "", "a" |
|
{n} |
精确匹配前面的子表达式 n 次。 | \d{3} 匹配3个连续的数字 |
{n,} |
匹配前面的子表达式至少 n 次。 | \d{2,} 匹配2个或更多连续的数字 |
{n,m} |
匹配前面的子表达式至少 n 次,但不超过 m 次。 | \d{2,4} 匹配2到4个连续的数字 |
| 当跟在 , , , , 后面时,表示非贪婪模式(匹配尽可能少的字符)。 | "<.*>" 贪婪模式会匹配 <div>hello</div>"<.*?>" 非贪婪模式会匹配 <div> 和 </div> |
|
\d |
匹配一个数字,等价于 [0-9]。 |
\d 匹配 '1', '2', '9' |
\D |
匹配一个非数字字符,等价于 [^0-9]。 |
\D 匹配 'a', ' ', '#' |
\w |
匹配单词字符(字母、数字、下划线),等价于 [a-zA-Z0-9_]。 |
\w 匹配 'a', 'B', '1', '_' |
\W |
匹配非单词字符,等价于 [^a-zA-Z0-9_]。 |
\W 匹配 '!', '@', ' ' |
\s |
匹配任何空白字符(空格、制表符、换行符等)。 | \s 匹配 ' ', '\t', '\n' |
\S |
匹配任何非空白字符。 | \S 匹配 'a', '1', '!' |
[...] |
字符集,匹配方括号内的任意一个字符。 | [abc] 匹配 'a', 'b', 或 'c' |
[^...] |
否定字符集,匹配不在方括号内的任意一个字符。 | [^abc] 匹配 'd', '1', '!' (只要不是a,b,c) |
| 捕获组,将括号内的子表达式作为一个整体,并记住匹配的内容。 | (\d{3})-(\d{4}) 可以分别捕获区号和号码 |
|
\ |
转义字符,用于转义特殊字符,使其字面化。 | \\ 匹配反斜杠 \\. 匹配点 |
完整代码示例
下面是一个综合示例,演示了 Pattern 和 Matcher 的多种用法。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexExample {
public static void main(String[] args) {
String text = "学习Java正则表达式,版本有Java 8, Java 11, Java 17,我的邮箱是test.user+alias@example.com,电话是138-1234-5678。";
// 1. 编译正则表达式
// 目标:查找所有 "Java" 后面跟着一个空格和一个数字的版本号
Pattern versionPattern = Pattern.compile("Java (\\d+)");
// 2. 创建 Matcher 对象
Matcher versionMatcher = versionPattern.matcher(text);
System.out.println("--- 查找所有Java版本号 ---");
// 3. 使用 find() 查找所有匹配项
while (versionMatcher.find()) {
// group(0) 是整个匹配的子串
// group(1) 是第一个捕获组的内容
System.out.println("找到版本: " + versionMatcher.group(0) +
", 具体数字: " + versionMatcher.group(1) +
", 起始位置: " + versionMatcher.start());
}
System.out.println("\n--- 验证整个字符串是否匹配邮箱格式 ---");
// 目标:验证一个字符串是否是有效的邮箱地址
String email1 = "test@example.com";
String email2 = "invalid-email";
String emailRegex = "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$";
Pattern emailPattern = Pattern.compile(emailRegex);
System.out.println("验证 '" + email1 + "': " + emailPattern.matcher(email1).matches());
System.out.println("验证 '" + email2 + "': " + emailPattern.matcher(email2).matches());
System.out.println("\n--- 提取电话号码 ---");
// 目标:提取符合 "XXX-XXXX-XXXX" 格式的电话号码
String phoneRegex = "(\\d{3})-(\\d{4})-(\\d{4})";
Pattern phonePattern = Pattern.compile(phoneRegex);
Matcher phoneMatcher = phonePattern.matcher(text);
if (phoneMatcher.find()) {
System.out.println("找到电话号码: " + phoneMatcher.group(0));
System.out.println("区号: " + phoneMatcher.group(1));
System.out.println("前缀: " + phoneMatcher.group(2));
System.out.println("后缀: " + phoneMatcher.group(3));
}
System.out.println("\n--- 使用 split 方法分割字符串 ---");
// 目标:用数字作为分隔符来分割字符串
String splitText = "abc123def456ghi";
Pattern splitPattern = Pattern.compile("\\d+");
String[] parts = splitPattern.split(splitText);
for (String part : parts) {
System.out.println("分割部分: " + part);
}
}
}
Pattern 的标志
Pattern.compile(String regex, int flags) 方法允许你传入标志来修改匹配行为,这些标志是 Pattern 类的静态常量。
| 标志 | 值 | 描述 |
|---|---|---|
CASE_INSENSITIVE |
2 |
匹配时不区分大小写。 |
MULTILINE |
8 |
影响 ^ 和 的行为,在此模式下,^ 匹配行的开头, 匹配行的结尾。 |
DOTALL |
32 |
让 匹配包括换行符在内的所有字符。 |
UNICODE_CASE |
64 |
与 CASE_INSENSITIVE 一起使用,进行 Unicode 大小写不敏感的匹配。 |
UNIX_LINES |
1 |
在多行模式下,只将 \n 识别为行终止符。 |
CANON_EQ |
128 |
考虑 Unicode 字符的规范等价性。 |
示例:使用 MULTILINE 标志
String multilineText = "第一行\n第二行\n第三行";
// 不使用 MULTILINE 标志
Pattern p1 = Pattern.compile("^第二行$");
System.out.println(p1.matcher(multilineText).find()); // false
// 使用 MULTILINE 标志
Pattern p2 = Pattern.compile("^第二行$", Pattern.MULTILINE);
System.out.println(p2.matcher(multilineText).find()); // true
最佳实践
- 预编译正则表达式:如果你的正则表达式会被多次使用(例如在循环中),一定要先编译成
Pattern对象并复用,而不是每次都调用String.matches()等方法,因为后者每次都会隐式地编译新的Pattern,效率较低。 - 使用原始字符串字面量:在 Java 中,反斜杠
\是一个转义字符,为了在正则表达式中使用\d,你需要在字符串字面量中写成"\\d",这非常容易出错,一个更好的方法是使用Pattern类的quote()方法来转义任何字面字符串,或者干脆在代码中处理好。 - 区分
matches(),lookingAt(), 和find():matches(): 整个字符串必须完全匹配。lookingAt(): 字符串开头部分匹配即可。find(): 在字符串中查找任意位置的子串匹配,最灵活,也最常用。
- 小心贪婪与非贪婪:默认情况下,量词(, , , )是贪婪的,会匹配尽可能多的字符,如果这不符合你的预期(例如解析HTML/XML标签),记得在量词后加上 使其变为非贪婪模式。

