Java 字符串转数字时,普通整数用 Integer.parseInt,确实需要包装对象时用 valueOf,金额和精确小数用字符串构造 BigDecimal。任何外部输入都要显式处理 NumberFormatException,不能把失败静默替换成 0;转换成功后还要继续检查业务范围。

本文依据 Java SE 25 API 编写,示例也适用于常见现代 JDK。目标不是背四个方法,而是让表单、配置和接口输入在空白、正负号、溢出、千分位、本地化及 BigDecimal 金额场景下都有明确结果。最后会给出一个统一但不过度“万能”的转换边界和测试清单。
一、先看结论:不同场景怎么选
| 输入场景 | 推荐方法 | 返回类型 | 失败处理 |
|---|---|---|---|
| 普通整数 | Integer.parseInt |
int |
捕获 NumberFormatException |
| 需要包装对象 | Integer.valueOf |
Integer |
捕获 NumberFormatException |
| 可能很大的整数 | Long.parseLong 或 BigInteger |
long / BigInteger |
避免溢出截断 |
| 金额、精确小数 | new BigDecimal(raw) |
BigDecimal |
明确小数位和舍入模式 |
| 本地化展示数字 | NumberFormat + 固定 Locale |
Number |
用 ParsePosition 完整消费 |
| 可缺失的字段 | 单独表示缺失 | 不适用 | 不与数值 0 混用 |
一句话:按业务类型选 int、long 或 BigDecimal;外部输入必须处理格式错误,再做范围校验。
二、parseInt 与 valueOf 的差别在返回类型
public class NumberDemo {
public static void main(String[] args) {
// 返回基本类型 int
int lessons = Integer.parseInt("12");
// 返回包装类型 Integer
Integer boxed = Integer.valueOf("12");
// 自动拆箱后相加
System.out.println(lessons + boxed);
}
}
预期结果(未在本机执行)是 24。Integer.parseInt 返回基本类型 int,valueOf 返回 Integer。自动拆箱让两者看起来相似,但集合、可空值和泛型场景才真正需要包装类型。Java 教程 可补基本类型与包装类知识。
| 对比项 | parseInt |
valueOf |
|---|---|---|
| 返回类型 | int |
Integer |
| 是否可能为 null | 否 | 是(若自行处理) |
| 适用场景 | 内部计算 | 集合、泛型、可空字段 |
| 自动装箱 | 不涉及 | 返回已装箱对象 |
不要为了“可能用到 null”一律选择 Integer,也不要为了少一个对象就拒绝包装类型。先由 API 语义决定返回值:计算内部通常用 int,缺失值需要单独表达时可以用 Integer 或 OptionalInt,但缺失与格式错误仍应分开。
三、非法格式和溢出必须进入失败分支
static Integer parseAge(String raw) {
// 空值或空白直接返回 null,表示缺失
if (raw == null || raw.isBlank()) return null;
try {
// 去除前后空格后解析整数
int value = Integer.parseInt(raw.trim());
// 业务范围检查:年龄必须在 0 到 150 之间
if (value < 0 || value > 150) {
throw new IllegalArgumentException("年龄越界");
}
return value;
} catch (NumberFormatException error) {
// 格式错误转换为带原始输入的业务异常
throw new IllegalArgumentException("年龄必须是整数: " + raw, error);
}
}
2147483648 虽然都是数字字符,却超出 int 上限,同样会抛 NumberFormatException。+12 和 -12 是合法十进制整数,但是否允许正负号由字段业务决定。业务校验应在语法转换之后继续做范围检查。
| 输入 | 结果 | 说明 |
|---|---|---|
"12" |
12 | 正常整数 |
" 12 " |
12 | 去除前后空格后合法 |
"+12" |
12 | 允许正号 |
"-12" |
-12 | 允许负号 |
"12x" |
抛异常 | 含非法字符 |
"2147483648" |
抛异常 | 超出 int 上限 |
"" |
返回 null | 缺失值,与 0 区分 |
常用 API 记不住时可以查 Java 速查手册。
如果输入可能超过 int,应明确改用 Long.parseLong 或 BigInteger,而不是捕获异常后截断。类型范围本身就是数据契约的一部分,接口文档、数据库字段和 Java 类型应保持一致。
四、金额要从字符串创建 BigDecimal
import java.math.BigDecimal;
import java.math.RoundingMode;
// 从字符串构造 BigDecimal,避免二进制浮点误差
BigDecimal price = new BigDecimal("19.90");
BigDecimal count = new BigDecimal("3");
// 相乘后保留两位小数,四舍五入
BigDecimal total = price.multiply(count).setScale(2, RoundingMode.HALF_UP);
// 使用 toPlainString 避免科学计数法
System.out.println(total.toPlainString());
预期结果是 59.70。不要先把文本变成 double 再交给 BigDecimal,因为二进制浮点近似可能已经进入对象。字符串构造能按十进制字符表达数值。
BigDecimal 金额的小数位、舍入模式和币种规则仍要由业务决定;不同币种并不都固定保留两位小数。
static BigDecimal parseMoney(String raw) {
// 空值直接拒绝
if (raw == null || raw.isBlank()) {
throw new IllegalArgumentException("金额不能为空");
}
try {
// 从字符串构造,并拒绝多余小数位
return new BigDecimal(raw.trim())
.setScale(2, RoundingMode.UNNECESSARY);
} catch (ArithmeticException | NumberFormatException error) {
// 格式错误或小数位过多时抛出业务异常
throw new IllegalArgumentException("金额必须是最多两位小数", error);
}
}
这里用 UNNECESSARY 是为了拒绝多余小数位,而不是悄悄四舍五入。若产品规则允许三位输入并四舍五入,就应明确换成约定的 RoundingMode,并增加相应测试。
| BigDecimal 操作 | 推荐写法 | 说明 |
|---|---|---|
| 创建 | new BigDecimal("19.90") |
从字符串构造,避免浮点误差 |
| 乘法 | price.multiply(count) |
精确计算 |
| 设置小数位 | .setScale(2, RoundingMode.HALF_UP) |
明确舍入规则 |
| 输出 | .toPlainString() |
避免科学计数法 |
| 拒绝多余小数 | RoundingMode.UNNECESSARY |
超出即抛异常 |
五、千分位和本地化数字不能直接 parseInt
"1,234"、"1 234" 和全角数字都不是 parseInt 的通用输入。只面向固定机器协议时,最好规定无分隔符格式;面向用户界面时,用 NumberFormat 并明确 Locale。
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Locale;
// 使用美式区域创建整数格式
NumberFormat format = NumberFormat.getIntegerInstance(Locale.US);
// 记录解析位置
ParsePosition pos = new ParsePosition(0);
Number value = format.parse("1,234", pos);
// 确认完整消费输入,避免部分解析
if (value == null || pos.getIndex() != "1,234".length()) {
throw new IllegalArgumentException("不是完整的美式整数");
}
System.out.println(value.longValue());
ParsePosition 能确认是否完整消费输入,避免 123abc 被部分解析。
| 输入 | Locale |
解析结果 | 说明 |
|---|---|---|---|
"1,234" |
Locale.US |
1234 | 逗号是千分位 |
"1.234" |
Locale.GERMANY |
1234 | 点是千分位 |
"1,234" |
Locale.FRANCE |
1.234 | 逗号是小数点 |
"123abc" |
任意 | 部分解析 | 必须用 ParsePosition 检查 |
练习普通语法时可在本地保存完整类并编译,金额与 Locale 用例则应进入项目测试而不是只看单次输出。
本地化解析还要固定 Locale,不能依赖服务器默认区域。同一个 1,234 在不同文化约定中可能代表千分位或小数分隔,因此 API 输入最好使用与区域无关的机器格式,只有界面展示层才根据用户区域解析与格式化。
六、转换函数要保留原始输入和业务语义
不要写一个“万能 parseNumber”同时猜整数、金额、百分比和科学计数法。先由字段语义决定目标类型,再转换。日志可以记录字段名和脱敏后的原始值,但密码、令牌和个人信息不能直接落盘。
用表格确定每种输入的契约
| 输入场景 | 推荐类型/方法 | 失败处理 |
|---|---|---|
| 订单数量 | Integer.parseInt |
拒绝非整数并检查范围 |
| 可能很大的编号 | Long.parseLong 或保持字符串 |
不允许溢出后截断 |
| 金额 | new BigDecimal(raw) |
明确小数位和舍入规则 |
| 本地化展示数字 | NumberFormat + 固定 Locale |
必须完整消费输入 |
| 可缺失的字段 | 单独表示缺失 | 不与数值 0 混用 |
把契约写成测试:
| 测试输入 | 预期结果 |
|---|---|
"0" |
返回 0 |
" 12 " |
返回 12 |
"12x" |
抛 NumberFormatException |
"2147483648" |
抛 NumberFormatException |
"19.90" |
BigDecimal 精确值 |
"19.999" |
拒绝或按规则舍入 |
测试不仅断言返回数字,也要断言异常类型与对用户可理解的消息。这样升级 JDK、修改输入清洗或替换接口时,转换行为不会悄悄变化。
批量导入还需要行号和字段名。单个转换函数可以抛出包含原始值的内部异常,导入层再补上 第 18 行 price 字段 等上下文,并决定立即停止还是收集全部错误。日志中保留定位所需信息,但身份证号、账号等敏感原文要脱敏。
对配置文件输入,不建议把所有错误都捕获后回退默认值。默认值只适用于“字段缺失且规范允许”的情况;字段存在但格式错误时应拒绝启动或明确告警,否则错误配置会以合法数字 0 继续运行。接口层也应区分 400 格式错误与服务器内部异常。
若转换位于高频路径,先用真实数据做基准,再讨论对象分配或缓存。绝大多数表单和配置解析的瓶颈不在 parseInt 与 valueOf 的差异,而在网络、数据库和校验流程。为了微小性能差异牺牲错误语义,通常得不偿失。
代码评审时可以追问三件事:
- 目标类型为什么足够承载业务范围;
- 空值、非法值与真实的 0 是否被区分;
- 错误消息是否能定位字段但不会泄露敏感输入。
只要其中一项回答不清楚,转换函数就还没有形成可靠契约。

总结
Java 字符串转数字的选择顺序很清楚:按业务类型选 int、long 或 BigDecimal;按是否需要对象选 Integer.parseInt 或 valueOf;对外部输入捕获格式错误,再做范围和业务校验。BigDecimal 金额从原始字符串构造,本地化数字固定 Locale。失败时给出可定位信息,不要返回一个真假难辨的 0。
延伸学习
- Java 字符串包含判断 可补字符串预处理;
- JAVA架构师从零开始学全套课程 适合继续整理类型、异常和集合基础。
常见问题
Q:Integer.valueOf 一定比 parseInt 慢吗?
A:不要凭返回类型下结论。两者先满足语义需求,性能差异只有在真实热点和基准测试中才值得优化。
Q:空字符串应该转换成 0 吗?
A:通常不应该。空值、格式错误和真实数值 0 是三种状态,混在一起会让后续校验失真。
Q:金额可以用 Double.parseDouble 吗?
A:展示或近似计算可以评估使用,但需要精确十进制规则的金额应优先使用 BigDecimal,并明确舍入策略。
Q:本地化数字解析为什么需要 ParsePosition?
A:NumberFormat.parse 可能只解析输入的一部分。ParsePosition 的 getIndex() 能确认是否完整消费,避免 123abc 被当成 123。

免费 AI IDE



