1. 概述

本文将介绍如何在 Java 中判断一个字符串是否同时包含大写字母、小写字母、数字以及特殊字符。这类需求常见于密码强度校验场景,属于实际开发中的高频“踩坑”点之一。

目标是确保字符串满足至少包含四类字符中的每一类。我们提供两种主流实现方式:正则表达式和原生 Java 方法,各有适用场景。


2. 使用正则表达式

正则表达式是处理字符串匹配最简洁的方式之一。对于这类校验逻辑,使用正则可以做到一行定义,终身受用✅。

由于正则模式是固定的,为了避免重复编译带来的性能损耗,建议提前编译并缓存 Pattern 实例。

private static final Pattern[] inputRegexes = new Pattern[4];

static {
    inputRegexes[0] = Pattern.compile(".*[A-Z].*");  // 至少一个大写字母
    inputRegexes[1] = Pattern.compile(".*[a-z].*");  // 至少一个小写字母
    inputRegexes[2] = Pattern.compile(".*\\d.*");     // 至少一个数字
    inputRegexes[3] = Pattern.compile(".*[`~!@#$%^&*()\\-_=+\\\\|\\[{\\]};:'\",<.>/?].*");  // 特殊字符
}

然后写一个通用方法进行批量匹配:

private static boolean isMatchingRegex(String input) {
    boolean inputMatches = true;
    for (Pattern inputRegex : inputRegexes) {
        if (!inputRegex.matcher(input).matches()) {
            inputMatches = false;
        }
    }
    return inputMatches;
}

⚠️ 注意:String.matches() 方法每次调用都会重新编译正则,性能较差,生产环境务必缓存 Pattern 对象。

2.1 单条正则表达式优化

上述方式虽然可读性强,但需要遍历多个 Pattern,效率略低。如果所有条件必须同时满足,推荐合并为一条正则,简单粗暴且性能更优✅。

String regex = "^(?=.*?\\p{Lu})(?=.*?\\p{Ll})(?=.*?\\d)" +
    "(?=.*?[`~!@#$%^&*()\\-_=+\\\\|\\[{\\]};:'\",<.>/?]).*$";

测试用例:

@Test
public void givenSingleRegex_whenMatchingCorrectString_thenMatches() {
    String validInput = "Ab3;";
    assertTrue(Pattern.compile(regex).matcher(validInput).matches());
}

🔍 关键点解析:

  • (?=.*\\p{Lu}):正向前瞻,确保存在至少一个大写Unicode字母(比 [A-Z] 更通用,支持非英语字符)
  • (?=.*\\p{Ll}):同理,匹配任意语言的小写字母
  • (?=.*\\d):确保包含数字
  • (?=.*?[特殊字符集]):前瞻特殊字符
  • ^.*$:从头到尾完整匹配

📌 优势:

  • 只需一次匹配,无需循环
  • 支持国际化字符(Unicode)
  • 更适合封装成通用工具类

3. 使用核心 Java 类(无正则)

如果你团队对正则有“PTSD”,或者追求极致可读性和可控性,也可以不用正则,纯靠 JDK 原生 API 实现。

核心思路:遍历字符串每个字符,逐个分类判断。

private static boolean checkString(String input) {
    String specialChars = "~`!@#$%^&*()-_=+\\|[{]};:'\",<.>/?";
    char currentCharacter;
    boolean numberPresent = false;
    boolean upperCasePresent = false;
    boolean lowerCasePresent = false;
    boolean specialCharacterPresent = false;

    for (int i = 0; i < input.length(); i++) {
        currentCharacter = input.charAt(i);
        if (Character.isDigit(currentCharacter)) {
            numberPresent = true;
        } else if (Character.isUpperCase(currentCharacter)) {
            upperCasePresent = true;
        } else if (Character.isLowerCase(currentCharacter)) {
            lowerCasePresent = true;
        } else if (specialChars.contains(String.valueOf(currentCharacter))) {
            specialCharacterPresent = true;
        }
    }

    return numberPresent && upperCasePresent && lowerCasePresent && specialCharacterPresent;
}

📌 说明:

  • Character.isDigit() / isUpperCase() / isLowerCase():JDK 内建方法,准确高效
  • ❌ 没有 isSpecialChar() 这种方法,所以需要手动维护特殊字符集合
  • ⚠️ specialChars.contains(String.valueOf(c)) 效率一般,若性能敏感可用 Set<Character> 替代

📌 适用场景:

  • 对正则有安全或维护顾虑的项目
  • 需要高度定制化逻辑(比如排除某些特殊字符)
  • 嵌入式或受限环境(正则引擎不可用)

4. 总结

方式 优点 缺点 推荐指数
多正则 + 缓存 可读性强,易于拆分复用 多次匹配,略慢 ⭐⭐⭐⭐
单条正则 性能高,一行搞定,支持 Unicode 可读性差,难调试 ⭐⭐⭐⭐⭐
原生 Java 遍历 逻辑清晰,不依赖正则 代码量多,维护特殊字符麻烦 ⭐⭐⭐

✅ 最佳实践建议:

  • 密码校验等通用场景 → 使用单条正则
  • 安全审计严格项目 → 提前编译 Pattern,避免运行时编译
  • 特殊字符集可提取为常量或配置项,便于管理

完整示例代码已托管至 GitHub:https://github.com/example/java-string-validation


原始标题:Java Check a String for Lowercase/Uppercase Letter, Special Character and Digit