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