1. 概述
在Java开发中,我们经常需要对字符串进行掩码处理,比如隐藏日志文件或用户界面中的敏感信息。本文将探讨几种简单高效的Java实现方案,重点保留字符串最后N个字符(本文以N=4为例)。
2. 问题引入
实际开发中,我们常需处理信用卡号、社保号或邮箱等敏感信息。常见需求是隐藏大部分字符,仅保留最后几位。以以下三个字符串为例:
static final String INPUT_1 = "a b c d 1234";
static final String INPUT_2 = "a b c d ";
static final String INPUT_3 = "a b";
目标是将每个字符串除最后4个字符外的所有字符替换为星号(*
),预期结果如下:
static final String EXPECTED_1 = "********1234";
static final String EXPECTED_2 = "******** ";
static final String EXPECTED_3 = "a b";
关键规则:
- ✅ 当字符串长度≤4时,直接返回原字符串
- ✅ 空白字符与普通字符同等处理
- ✅ 掩码字符统一使用
*
接下来我们将用不同方案实现该功能,并通过单元测试验证结果。
3. 使用字符数组方案
字符串本质是字符序列,可通过字符数组操作实现掩码:
String maskByCharArray(String input) {
if (input.length() <= 4) {
return input;
}
char[] chars = input.toCharArray();
Arrays.fill(chars, 0, chars.length - 4, '*');
return new String(chars);
}
实现解析:
- 先检查长度≤4的情况
- 将字符串转为字符数组
- 使用
Arrays.fill()
填充掩码字符:- 从索引0开始
- 填充至
length-4
的位置
- 将处理后的数组转回字符串
测试验证:
assertEquals(EXPECTED_1, maskByCharArray(INPUT_1));
assertEquals(EXPECTED_2, maskByCharArray(INPUT_2));
assertEquals(EXPECTED_3, maskByCharArray(INPUT_3));
4. 双子字符串方案
将字符串拆分为两部分:待掩码部分和保留部分:
String maskBySubstring(String input) {
if (input.length() <= 4) {
return input;
}
String toMask = input.substring(0, input.length() - 4);
String keepPlain = input.substring(input.length() - 4);
return toMask.replaceAll(".", "*") + keepPlain;
}
关键步骤:
- 长度检查(同上)
- 提取两个子字符串:
toMask
:前length-4
个字符keepPlain
:最后4个字符
- 使用
replaceAll()
替换掩码部分:- ⚠️
.
是正则表达式元字符,匹配任意字符
- ⚠️
- 拼接处理后的两部分
测试结果:
assertEquals(EXPECTED_1, maskBySubstring(INPUT_1));
assertEquals(EXPECTED_2, maskBySubstring(INPUT_2));
assertEquals(EXPECTED_3, maskBySubstring(INPUT_3));
5. 正则表达式方案
利用正则表达式的前瞻断言(lookahead)特性,一步完成掩码:
String maskByRegex(String input) {
if (input.length() <= 4) {
return input;
}
return input.replaceAll(".(?=.{4})", "*");
}
正则解析:
.
:匹配任意字符(?=.{4})
:正向零宽断言,确保当前位置后恰好有4个字符- 整体效果:替换所有"后面还有4个字符"的字符
测试验证:
assertEquals(EXPECTED_1, maskByRegex(INPUT_1));
assertEquals(EXPECTED_2, maskByRegex(INPUT_2));
assertEquals(EXPECTED_3, maskByRegex(INPUT_3));
6. 使用repeat()方法(Java 11+)
Java 11引入的repeat()
方法可简化掩码字符生成:
String maskByRepeat(String input) {
if (input.length() <= 4) {
return input;
}
int maskLen = input.length() - 4;
return "*".repeat(maskLen) + input.substring(maskLen);
}
实现要点:
- 计算掩码长度
maskLen
- 用
"*".repeat(maskLen)
生成掩码字符串 - 拼接保留部分
input.substring(maskLen)
测试结果:
assertEquals(EXPECTED_1, maskByRepeat(INPUT_1));
assertEquals(EXPECTED_2, maskByRepeat(INPUT_2));
assertEquals(EXPECTED_3, maskByRepeat(INPUT_3));
7. 总结
我们对比了四种字符串掩码方案:
方案 | 优点 | 适用场景 |
---|---|---|
字符数组 | 直观高效 | 所有Java版本 |
双子字符串 | 逻辑清晰 | 需要分段处理时 |
正则表达式 | 代码简洁 | 复杂模式匹配 |
repeat()方法 | 最简洁 | Java 11+环境 |
实际应用时:
- ✅ 可灵活调整掩码字符(如
#
)和保留位数(N值) - ✅ 所有方案均需注意空字符串处理
- ❌ 避免在超长字符串中使用正则方案(性能问题)
完整代码示例可参考:GitHub仓库