string.replace()不支持正则,只能字面替换;正则脱敏须用replaceall()或replacefirst(),推荐预编译pattern复用,并注意边界控制、分组引用、回溯优化及null安全。
string.replace() 本身不支持正则表达式,它只做字面量(literal)的全字符串替换。想用正则实现脱敏,必须改用 string.replaceall() 或 string.replacefirst() —— 它们才真正接受正则模式。
为什么不能用 replace() 做正则脱敏
String.replace(CharSequence target, CharSequence replacement) 把第一个参数当作普通字符串处理,不会编译为正则。比如:
String s = "phone: 13812345678";
s.replace("\d{11}", "***"); // ❌ 什么也不会替换,因为 d{11} 被当字面字符串找
它实际是在找字符串 "\d{11}" 这 7 个字符,而非匹配 11 位数字。
正确做法:用 replaceAll() + 预编译 Pattern 提升性能
高频脱敏场景(如日志、接口响应)中,反复调用 replaceAll() 会重复编译正则,造成开销。推荐预编译 Pattern 并复用:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 定义静态 final Pattern,避免每次新建编译对象
- 用
matcher(input).replaceAll(replacement)替代字符串方法,更可控 - 对手机号、身份证、邮箱等常见敏感字段,分别建立专用 Pattern
private static final Pattern MOBILE_PATTERN =
Pattern.compile("(1[3-9]\d{9})"); // 匹配标准大陆手机号
public static String maskMobile(String text) {
return MOBILE_PATTERN.matcher(text).replaceAll("1XXXXXXXXXX");
}
脱敏时的关键细节注意
-
边界控制:用
\b或(? 防止误匹配(如避免把 "123456789012" 中间一段当成手机号) -
分组捕获再引用:保留前后文结构,例如
replace("(.{3})\d{4}(.{4})", "$1****$2")脱敏银行卡 -
避免回溯灾难:不用
.*匹配长文本;身份证号用\d{17}[\dxX]而非\d{15,18} -
空值与 null 安全:调用前判空,或用
Objects.toString(s, "")统一处理
简单但实用的脱敏工具示例
一个轻量封装,支持手机号、邮箱、身份证三类基础脱敏:
public class TextMasker {
private static final Pattern PHONE = Pattern.compile("(1[3-9]\d{2})\d{4}(\d{4})");
private static final Pattern EMAIL = Pattern.compile("(\w+)@([\w.]+)");
private static final Pattern ID_CARD = Pattern.compile("(\d{4})\d{10}([\dxX])");
public static String mask(String input) {
if (input == null) return null;
return ID_CARD.matcher(PHONE.matcher(EMAIL.matcher(input)
.replaceAll("$1@***")).replaceAll("$1****$2"))
.replaceAll("$1****$2");
}
}
注意:链式调用时顺序影响结果,建议按字段长度或唯一性从高到低处理(如先身份证,再手机号,最后邮箱),减少干扰。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










