应对外部字符串转换风险需三步:先防御性清洗(判空、trim、BOM处理),再轻量正则预筛(如"-?d+"),最后封装返回Optional或带语义默认值的安全方法,并记录脱敏日志与监控告警。

外部接口传入的字符串不可信,直接调用 Integer.parseInt() 或 Double.parseDouble() 极易因非法格式、空值、超范围等问题触发 NumberFormatException,导致服务崩溃或逻辑中断。关键不是“避免异常”,而是让转换过程可控、可预期、可追溯。
先做防御性清洗和判空
很多崩溃其实源于最基础的疏忽:没处理 null、空白、BOM 字符(如 u2028)、不可见分隔符等。这些字符肉眼难见,但 parseInt 会直接拒绝。
- 始终对原始字符串调用
str == null ? null : str.trim()—— 注意trim()能清除包括u2028、u2029、u00a0在内的 25 种 Unicode 空白字符 - 清洗后立即检查是否为空:
if (cleaned.isEmpty()) return null;,不进 parse 流程 - 若业务允许前导/后缀空格,也建议统一 trim,避免 JDK 版本或 Android Runtime 差异引发不稳定
用正则做轻量级格式预筛
正则不能替代异常捕获(比如超大数仍会抛异常),但能快速拦截明显非法输入,减少 try-catch 频次,尤其在高并发解析场景下提升性能。
- 整数校验:
str.matches("-?\d+")(支持负号) - 浮点数校验:
str.matches("-?\d+(\.\d+)?")(简单小数)或更严谨的"-?\d*\.?\d+" - 注意:正则匹配成功 ≠ 解析一定成功(例如
"99999999999999999999"匹配整数正则,但远超int范围)
封装安全转换方法,明确失败语义
裸写 try-catch 容易被滥用(比如吞异常后返回 0,导致“真值为 0”和“转换失败”无法区分)。应封装成有明确契约的工具方法。
立即学习“Java免费学习笔记(深入)”;
- 返回
Optional<Integer>:调用方必须显式处理空值,语义清晰return Optional.ofNullable(str).map(s -> s.trim()).filter(s -> !s.isEmpty()).filter(s -> s.matches("-?\d+")).map(Integer::parseInt).orElse(null); - 或提供默认值入口:
parseIntOrDefault(str, -1),但需文档注明该默认值是业务占位符,非真实数据 - 严禁在 catch 中静默返回魔数(如 0、-1、999)而不加注释或日志
记录上下文并告警,而非仅打印堆栈
生产环境崩溃往往不是因为一次异常,而是异常反复出现却无人知晓。转换失败时应留下可定位的信息。
- 记录原始字符串(脱敏后)、来源接口名、时间戳、traceId
- 对高频失败字段(如某订单号字段连续 10 次非法)触发监控告警,提示上游数据质量恶化
- 避免只写
e.printStackTrace()—— 日志系统通常不采集控制台输出,且无上下文


















