decimalformat默认使用银行家舍入法(half_even),非数学四舍五入;需显式设roundingmode.half_up才实现常规四舍五入;零值显示依赖pattern中0(必显)或#(有则显);非线程安全,须避免共享实例;parse()返回类型不固定,推荐用doublevalue()或bigdecimalvalue()安全取值。

DecimalFormat 默认会四舍五入,但不是“数学四舍五入”
很多人以为 DecimalFormat 用 "0.00" 就能稳稳得到四舍五入结果,结果发现 2.555 格式化成 2.55 —— 它其实用的是「银行家舍入法」(HALF_EVEN),也就是遇到 .5 时往偶数靠。比如 2.5 → 2,3.5 → 4。
如果你要严格四舍五入(即 HALF_UP),必须显式设置:
DecimalFormat df = new DecimalFormat("0.00");
df.setRoundingMode(RoundingMode.HALF_UP);- 不设
setRoundingMode,JDK 7+ 默认是RoundingMode.HALF_EVEN -
RoundingMode.HALF_UP才是日常说的“四舍五入”,1.235 → 1.24 - 别用
Math.round()预处理再转字符串——它对负数行为和DecimalFormat不一致,容易出错
pattern 中的 # 和 0 看似差不多,但零值表现完全不同
"0.00" 和 "#.##" 都能显示两位小数,但输入是 0 时:"0.00" → "0.00","#.##" → "0"。这不是 bug,是设计逻辑:0 表示「必须占位」,# 表示「有才显示」。
- 要强制保留两位小数(如金额显示),必须用
"0.00"或"0.00#" -
"#.##"在科学计算或日志里更省空间,但不适合财务场景 - pattern 里不能写
"0.##"来“兜底两位”——它等价于"#.##",前导 0 只在整数部分生效
多线程下直接复用 DecimalFormat 实例会出问题
DecimalFormat 不是线程安全的。如果你把它声明为 static 成员、全局单例,又在多个线程里同时调用 format() 或 parse(),可能返回错乱结果,比如 123.45 变成 12.345 或抛 NumberFormatException。
立即学习“Java免费学习笔记(深入)”;
- 最稳妥做法:每次用都 new 一个(小对象,GC 压力不大)
- 若追求性能,可用
ThreadLocal<decimalformat></decimalformat>缓存,但注意别泄露线程(尤其在线程池中) - 别试图加 synchronized——锁粒度太粗,反而拖慢吞吐
format() 返回 String,但 parse() 可能返回 BigDecimal 或 Double,类型不统一
df.parse("123.45") 默认返回 Number,实际是 Double;但如果 pattern 含科学计数法或数值极大,可能变成 BigDecimal。你若直接强转 (Double),运行时就崩。
- 安全做法是调用
parse(...).doubleValue()或.bigDecimalValue()显式取值 - 如果后续要做精确计算(比如金额加减),parse 后立刻转
BigDecimal,别用Double——浮点误差藏得深 -
parse()对空格、逗号、货币符号很敏感,输入不规范就抛ParseException,别指望它自动容错
DecimalFormat 的坑不在语法难,而在默认行为和边界条件——比如零值、负数、并发、解析类型,全得手动对齐预期。漏掉一个,线上就可能少一笔钱或多一位小数。










