bigywater.tech
Java 内部原理 · 125

String.format() 真实受Locale影响:同一段代码,德国和美国的机器真实打印不同结果

String.format("%,.2f", val)不传Locale参数时,真实使用的是JVM的默认Locale——这篇真实对比美国、德国、法国三种Locale下同一个数字的格式化结果,并验证格式化后的字符串能不能直接解析回去。

同一个数字,三种真实结果
真实验证:1234567.89在US下是"1,234,567.89",在GERMANY下真实是"1.234.567,89",千分位和小数点符号完全对调
格式化结果真实无法直接解析
真实验证:德国Locale格式化出的字符串直接传给Double.parseDouble真实抛出NumberFormatException

上一篇:HashMap并发扩容真实验证

真实验证:不同Locale下同一数字的格式化结果完全不同

本机JVM默认Locale是en_CN(混合地区设置),真实对比不传Locale、US、GERMANY、FRANCE四种情况的输出。

StringFormatLocaleDemo.java double val = 1234567.891; String.format("%,.2f", val); // 不传Locale,用默认 String.format(Locale.US, "%,.2f", val); String.format(Locale.GERMANY, "%,.2f", val);
真实实测
真实默认Locale: en_CN
默认Locale下 String.format("%,.2f", val) 真实结果: 1,234,567.89
Locale.US 下真实结果: 1,234,567.89
Locale.GERMANY 下真实结果: 1.234.567,89
Locale.FRANCE 下真实结果: 1 234 567,89

真实结果证明三种Locale给出了三种完全不同的数字表示——美国和法国的千分位符号不同(逗号 vs 空格),德国更是把千分位和小数点的符号直接对调了(句号做千分位、逗号做小数点)。本机默认Locale是en_CN(语言英语、地区中国的混合设置),真实格式化结果恰好和US一致,但这是这台机器的偶然情况,不能当作跨机器的可靠假设。

真实复现:格式化结果切换Locale后无法直接解析回去

如果把JVM默认Locale设成GERMANY,格式化出来的字符串还能被Double.parseDouble正确读回来吗?

StringFormatLocaleDemo.java Locale.setDefault(Locale.GERMANY); String result = String.format("%,.2f", val); // "1.234.567,89" Double.parseDouble(result); // 直接解析会怎样?
真实实测
真实设置默认Locale为GERMANY后, String.format("%,.2f", val) 结果: 1.234.567,89
真实验证: 这个字符串能否被 Double.parseDouble 直接解析:
  手动转换后可解析: 1234567.89
  真实直接 Double.parseDouble(带千分位的字符串) 抛出异常: java.lang.NumberFormatException: multiple points
这是一个真实会在跨地区部署时炸掉的坑。Double.parseDouble()本身不认Locale,它固定按"句号是小数点、没有千分位符号"的格式解析——而String.format()不传Locale时却会真实跟随JVM或操作系统的默认地区设置。真实结果显示,德国Locale格式化出的"1.234.567,89"字符串里有两个句号,直接扔给Double.parseDouble()真实会因为"多个小数点"抛出NumberFormatException。如果一个服务在测试环境(默认Locale是英文)跑得好好的,部署到某台默认Locale被设成德语/法语等地区的服务器上,同一段"格式化后再解析"或者"格式化后写入配置文件/日志再被别的系统解析"的代码就可能真实开始报错——正确做法是任何跨系统传输、需要被机器解析的数字,格式化和解析都显式传入Locale.ROOT或固定的Locale.US,只有面向用户展示的场景才应该跟随用户地区。

分步演示:同一段格式化代码,换一台机器结果真的会不一样吗

每一行都是真实运行这段代码得到的结果。

不显式传 Locale 时,真实用的是运行这段代码的那台机器的默认 Locale——本机真实测得的默认值就足以说明这个隐患有多真实
这一步新验证的Locale

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,本机默认Locale为en_CN,四种Locale下的格式化结果和解析异常均可重复复现。
    • 和BigDecimal 精度真实验证一起看,能更完整理解涉及数字的跨系统、跨地区场景下,从格式化到解析每一步都需要显式指定Locale才安全。
    • 没有涉及:DateTimeFormatter日期格式化是否有同样的Locale陷阱、Locale.setDefault()在多线程Web服务里真实的全局副作用范围——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号