numberformat是抽象基类,提供locale感知的工厂方法;decimalformat是其具体实现,通过pattern支持精细定制。两者均非线程安全,parse()需配合parseposition校验完整性,bigdecimal可避免精度丢失。

Java 中的 NumberFormat 和 DecimalFormat 是处理数字显示与解析的核心工具,它们分工明确:前者是抽象基类,提供跨区域通用接口;后者是其最常用的具体实现,支持精细定制。用对了,能轻松适配多语言环境、控制小数位、添加千分位、转百分比或货币——用错了,容易出现线程安全问题、解析异常或格式不一致。
NumberFormat:面向场景的工厂式使用
NumberFormat 本身不能 new,必须通过静态工厂方法获取实例。它屏蔽了底层差异,让你专注业务语义:
- getNumberInstance():通用数字格式,自动适配当前 Locale 的小数点和分隔符(如中文环境用“,”作千分位,“.”作小数点)
- getIntegerInstance():强制舍去小数部分,适合计数类显示(如“1234567”不带小数、不带逗号)
- getCurrencyInstance():按系统或指定 Locale 返回货币格式(如 ¥1,234.56 或 $1,234.56)
- getPercentInstance():将 0.87 格式化为 “87%”,内部自动 ×100 并加 % 符号
- 所有实例都支持 setMinimumFractionDigits 和 setMaximumFractionDigits 控制小数位数,也支持 setGroupingUsed(false) 关闭千分位
DecimalFormat:面向模式的精准控制
当你需要自定义符号、固定宽度、前导零、科学计数法或条件格式(如正负不同前缀),就得用 DecimalFormat。它通过 pattern 字符串驱动,例如:
-
"#,##0.00"→ “1,234.56”(千分位 + 两位小数) -
"00000.00"→ “01234.56”(不足位补零) -
"#,##0.##;(#,##0.##)"→ 正数正常显示,负数加括号(如 “123.45” 和 “(123.45)”) -
"0.00%"→ 将 0.123 格式化为 “12.30%”(注意:这里不自动 ×100,需手动处理数值) - pattern 中单引号
'可转义特殊字符,如"'¥'#,##0.00"固定显示 ¥ 符号
解析与格式化的常见陷阱
两者都支持 format() 和 parse(),但实际使用中几个细节极易出错:
- parse() 不严格校验结尾:对字符串 "123abc",parse() 默认只取开头合法部分返回 123,不会报错;需配合 ParsePosition 使用才能判断是否完整匹配
- 线程不安全:NumberFormat 和 DecimalFormat 实例都不是线程安全的。高并发下应避免复用同一实例,推荐每次使用 new 或用 ThreadLocal 缓存
- Locale 不一致导致解析失败:用 Locale.US 格式化的 "1,234.56",若用 Locale.CHINA 的 NumberFormat 去 parse,会因千分位符号不识别而抛 NumberFormatException
- BigDecimal 更稳妥:当原始数据是 BigDecimal,优先用其自带的setScale()配合 DecimalFormat,避免 double 精度丢失(如 0.1 + 0.2 ≠ 0.3)
何时选哪个?一句话判断
如果只是按地区显示整数、货币、百分比,或者简单控小数位,用 NumberFormat 工厂方法最省心;如果要写报表、导出 Excel、做前端对齐展示,或需统一前导零、条件样式、自定义符号,那就直接上 DecimalFormat,用 pattern 定义行为,更可控也更灵活。










