1. 引言

在日常开发中,我们经常需要判断一个 String 是否为合法的数字。比如处理用户输入、解析配置文件或接口参数校验时,这种需求非常普遍。

本文将系统性地介绍多种判断字符串是否为数字的方法:从原生 Java 实现,到正则表达式,再到 Apache Commons 等第三方库的使用。

最后还会通过基准测试对比性能,帮你选出最适合实际场景的方案,避免踩坑。


2. 前置准备

在使用 Apache Commons 工具库之前,先引入依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

最新版本可前往 Maven Central 查看。


3. 使用原生 Java 方法

最简单粗暴的方式就是尝试解析,利用 Java 内置的 parse 方法配合异常处理。

常用方法包括:

  • Integer.parseInt(String)
  • Float.parseFloat(String)
  • Double.parseDouble(String)
  • Long.parseLong(String)
  • new BigInteger(String)

如果这些方法不抛出 NumberFormatException,说明字符串是合法数字。

✅ 推荐使用 Double.parseDouble() 覆盖整数、小数、科学计数法等大多数情况:

public static boolean isNumeric(String strNum) {
    if (strNum == null) {
        return false;
    }
    try {
        Double.parseDouble(strNum);
    } catch (NumberFormatException e) {
        return false;
    }
    return true;
}

✅ 示例测试

assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue(); 
assertThat(isNumeric("10.0d")).isTrue();
assertThat(isNumeric("   22   ")).isTrue(); // trim 后可解析

assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("")).isFalse();
assertThat(isNumeric("abc")).isFalse();

⚠️ 注意:Double.parseDouble() 会自动忽略前后空格(类似 trim),但中间有空格仍会失败。


4. 使用正则表达式

正则也是一种常见手段,适合对格式有明确要求的场景。

推荐正则:^-?\\d+(\\.\\d+)?$

🔍 正则解析

  • -?:可选负号
  • \\d+:一个或多个数字
  • (\\.\\d+)?:可选的小数部分(点 + 数字)

完整实现:

private static final Pattern NUMERIC_PATTERN = Pattern.compile("^-?\\d+(\\.\\d+)?$");

public static boolean isNumeric(String strNum) {
    if (strNum == null) {
        return false;
    }
    return NUMERIC_PATTERN.matcher(strNum).matches();
}

✅ 测试用例

assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue();

assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("abc")).isFalse();
assertThat(isNumeric(" 22 ")).isFalse(); // ❌ 有空格就不匹配
assertThat(isNumeric("10.0d")).isFalse(); // ❌ 不支持类型后缀

❌ 缺点:无法处理科学计数法、类型后缀(如 d, f, L),且性能较差。


5. 使用 Apache Commons

Apache Commons Lang3 提供了多个开箱即用的工具方法,语义清晰,性能优秀。

5.1. NumberUtils.isCreatable(String)

判断字符串是否为“可创建”的数字,支持范围最广:

  • 十六进制(0xFF
  • 八进制(07
  • 科学计数法(2.99e+8
  • 类型后缀(10.0d, 100L

但 ❌ 不接受前后空格。

assertThat(NumberUtils.isCreatable("22")).isTrue();
assertThat(NumberUtils.isCreatable("5.05")).isTrue();
assertThat(NumberUtils.isCreatable("10.0d")).isTrue();
assertThat(NumberUtils.isCreatable("0xFF")).isTrue();
assertThat(NumberUtils.isCreatable("07")).isTrue();
assertThat(NumberUtils.isCreatable("2.99e+8")).isTrue();

assertThat(NumberUtils.isCreatable(" 22 ")).isFalse(); // ❌ 空格不行
assertThat(NumberUtils.isCreatable("09")).isFalse();   // ❌ 09 不是合法八进制

✅ 适用场景:需要兼容多种数字格式,且能确保输入已 trim


5.2. NumberUtils.isParsable(String)

仅判断是否能被标准 parse 方法解析(如 Integer.parseInt)。

✅ 支持:

  • 正负整数、小数
  • 09 被视为十进制(不是八进制)

❌ 不支持:

  • 十六进制、科学计数法
  • 类型后缀(d, f, L
assertThat(NumberUtils.isParsable("22")).isTrue();
assertThat(NumberUtils.isParsable("09")).isTrue();     // ✅ 视为十进制
assertThat(NumberUtils.isParsable("6.2f")).isFalse();  // ❌ 不支持 f
assertThat(NumberUtils.isParsable("0xFF")).isFalse();  // ❌ 不支持 hex

✅ 适用场景:替代手动 try-catch,语义更清晰,性能更好。


5.3. StringUtils.isNumeric(CharSequence)

严格判断是否全为 Unicode 数字字符。

✅ 特点:

  • 支持多语言数字(如阿拉伯文 ١٢٣、梵文 १२३
  • 不接受小数点、正负号、空格
assertThat(StringUtils.isNumeric("123")).isTrue();
assertThat(StringUtils.isNumeric("١٢٣")).isTrue(); // 阿拉伯数字
assertThat(StringUtils.isNumeric("१२३")).isTrue(); // 梵文数字

assertThat(StringUtils.isNumeric("12.3")).isFalse(); // ❌ 小数点不行
assertThat(StringUtils.isNumeric("-123")).isFalse(); // ❌ 负号不行
assertThat(StringUtils.isNumeric("12 3")).isFalse(); // ❌ 空格不行

⚠️ 注意:这是“纯数字字符”判断,不是“是否为数字值”。


5.4. StringUtils.isNumericSpace(CharSequence)

isNumeric 的增强版,允许包含空格(包括中间空格)。

assertThat(StringUtils.isNumericSpace("123")).isTrue();
assertThat(StringUtils.isNumericSpace("12 3")).isTrue(); // ✅ 允许空格
assertThat(StringUtils.isNumericSpace("  ")).isTrue();   // ✅ 纯空格也行
assertThat(StringUtils.isNumericSpace("")).isTrue();     // ✅ 空字符串也行

assertThat(StringUtils.isNumericSpace("12.3")).isFalse(); // ❌ 仍不支持小数
assertThat(StringUtils.isNumericSpace("-123")).isFalse(); // ❌ 不支持符号

✅ 适用场景:用户输入可能包含空格分隔的数字串,但不允许其他符号。


6. 性能基准测试

光讲功能不够直观,来看性能对比。

测试环境:JMH,Java 17,20 次迭代。

6.1. 简单基准(单一输入:Integer.MAX_VALUE

方法 耗时 (ns/op)
usingCoreJava 57.241
NumberUtils.isCreatable 26.711 ✅
NumberUtils.isParsable 46.577
usingRegularExpressions 101.580 ❌
StringUtils.isNumeric 35.885
StringUtils.isNumericSpace 31.979

📌 结论:正则最慢,isCreatable 最快。


6.2. 增强基准(混合输入:95% 数字 + 5% 非法)

方法 耗时 (ns/op)
usingCoreJava 10162.872 ❌
NumberUtils.isCreatable 1703.243
NumberUtils.isParsable 1589.915 ✅
usingRegularExpressions 7168.761
StringUtils.isNumeric 1071.753 ✅
StringUtils.isNumericSpace 1157.722 ✅

📌 关键发现:

  • 原生 try-catch 在异常频繁时性能暴跌(异常处理开销大)
  • StringUtils.isNumeric 成为性能冠军
  • 正则虽慢,但比原生方法稳定

结论:输入合法率高时,原生方法尚可;但异常较多时,优先选 Commons 工具类。


7. 总结

方法 优点 缺点 推荐场景
Double.parseDouble() 简单直接 异常开销大 输入基本合法
正则 可控格式 性能差,难维护 格式严格固定
NumberUtils.isCreatable 功能全 不支持空格 多格式兼容
NumberUtils.isParsable 语义清晰 不支持扩展格式 替代 try-catch
StringUtils.isNumeric 性能极佳 仅限纯数字 高频调用场景

最终建议

  • 日常开发优先使用 NumberUtils.isParsable()isCreatable()
  • 高性能场景可考虑 StringUtils.isNumeric() + 预处理
  • 避免在高频路径使用正则或 try-catch 判断

所有示例代码已上传至 GitHub:https://github.com/yourname/java-string-numeric-check


原始标题:Check If a String Is Numeric in Java