數字格式化是國際化應用的核心需求。Java 提供 NumberFormat 抽象層與 DecimalFormat 具體實作,支援區域敏感的數字、貨幣、百分比、科學記號格式化與解析。本文系統整理核心 API、常見模式、區域化處理與最佳實務。
NumberFormat (abstract)
├── DecimalFormat // 數字/貨幣/百分比/科學記號
├── ChoiceFormat // 區間對應格式
└── CompactNumberFormat // Java 12+ 簡潔格式 (1.2K, 3.4M)
// 1. 取得預設區域的數字格式化器(實為 DecimalFormat)
NumberFormat nf = NumberFormat.getInstance(); // 一般數字
NumberFormat cf = NumberFormat.getCurrencyInstance(); // 貨幣
NumberFormat pf = NumberFormat.getPercentInstance(); // 百分比
// 2. 指定 Locale(跨區域顯示)
NumberFormat deFormat = NumberFormat.getInstance(Locale.GERMANY); // 1.234,56
NumberFormat usFormat = NumberFormat.getInstance(Locale.US); // 1,234.56
// 3. 直接使用 DecimalFormat 完全控制模式
DecimalFormat df = new DecimalFormat("#,##0.00"); // 模式字串
DecimalFormat df2 = (DecimalFormat) NumberFormat.getInstance(Locale.TAIWAN);
df2.applyPattern("#,##0.00"); // 結合區域預設與自訂模式
| 符號 | 含義 | 範例輸入 | 範例輸出 |
|---|---|---|---|
0 | 數字,不足補 0 | new DecimalFormat("0.00").format(1.2) | 1.20 |
# | 數字,不足不補 | new DecimalFormat("#.##").format(1.2) | 1.2 |
. | 小數點(依區域轉換) | DE: 1.234,56 | US: 1,234.56 | - |
, | 分組分隔符(依區域轉換) | df.setGroupingSize(3) | 1,234,567 |
% | 百分比(乘 100 加 %) | new DecimalFormat("##.0%").format(0.123) | 12.3% |
E | 科學記號 | new DecimalFormat("0.00E0").format(1234) | 1.23E3 |
¤ | 貨幣符號 | new DecimalFormat("¤#,##0.00").format(1234.5) | NT$1,234.50 |
'text' | 字面文字 | new DecimalFormat("'Year:' yyyy").format(...) | - |
DecimalFormat df = new DecimalFormat("0.00"); // 強制兩位小數
System.out.println(df.format(1.2)); // 1.20
System.out.println(df.format(1.234)); // 1.23 (四捨五入)
System.out.println(df.format(1)); // 1.00
// 方式一:模式含 %
DecimalFormat pct1 = new DecimalFormat("##.00%");
System.out.println(pct1.format(0.1234)); // 12.34%
System.out.println(pct1.format(1.0)); // 100.00%
// 方式二:手動乘 100 + 加號
DecimalFormat pct2 = new DecimalFormat("##.00");
System.out.println(pct2.format(0.1234 * 100) + "%"); // 12.34%
DecimalFormat df = new DecimalFormat("#,###");
df.setGroupingSize(3); // 標準三位一撇
System.out.println(df.format(123456789)); // 123,456,789
// 印度計數法:萬/億分組 (3,2,2...)
df.setGroupingSize(2); // 僅作示範,實際需更複雜邏輯
System.out.println(df.format(123456789)); // 1,23,45,67,89
// 禁用分組
df.setGroupingUsed(false);
System.out.println(df.format(123456789)); // 123456789
NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.TAIWAN);
System.out.println(currency.format(1234567.89)); // NT$1,234,568
// 精確控制:不四捨五入分
DecimalFormat currencyDf = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.US);
currencyDf.setMaximumFractionDigits(2);
currencyDf.setMinimumFractionDigits(2);
currencyDf.setRoundingMode(RoundingMode.DOWN); // 截斷不進位
System.out.println(currencyDf.format(123.456)); // $123.45
DecimalFormat sci = new DecimalFormat("0.00E00");
System.out.println(sci.format(12345)); // 1.23E04
System.out.println(sci.format(0.00123)); // 1.23E-03
NumberFormat nf = NumberFormat.getInstance(Locale.GERMANY);
try {
Number num = nf.parse("1.234,56"); // 德式:點分組、逗號小數
System.out.println(num.doubleValue()); // 1234.56
System.out.println(num.getClass()); // java.lang.Double
} catch (ParseException e) {
e.printStackTrace();
}
// 解析為特定型別
Long l = nf.parse("1234", new ParsePosition(0)).longValue();
double value = 1234567.891;
Locale[] locales = {Locale.US, Locale.GERMANY, Locale.TAIWAN, Locale.CHINA, Locale.JAPAN};
for (Locale loc : locales) {
NumberFormat nf = NumberFormat.getInstance(loc);
NumberFormat cf = NumberFormat.getCurrencyInstance(loc);
NumberFormat pf = NumberFormat.getPercentInstance(loc);
System.out.printf("%-10s | 數字: %-20s | 貨幣: %-20s | 百分比: %s%n",
loc.getDisplayName(), nf.format(value), cf.format(value), pf.format(0.1234));
}
輸出範例:
美國 | 數字: 1,234,567.891 | 貨幣: $1,234,567.89 | 百分比: 12%
德國 | 數字: 1.234.567,891 | 貨幣: 1.234.567,89 € | 百分比: 12%
台灣 | 數字: 1,234,567.891 | 貨幣: NT$1,234,568 | 百分比: 12%
中國 | 數字: 1,234,567.891 | 貨幣: ¥1,234,567.89 | 百分比: 12%
日本 | 數字: 1,234,567.891 | 貨幣: ¥1,234,568 | 百分比: 12%
DecimalFormat 非執行緒安全。多執行緒環境下:
ThreadLocal<DecimalFormat> 快取synchronized(df)
// 不好:每次新建
String s = new DecimalFormat("#,##0.00").format(value);
// 好:靜態常數重用(單執行緒/請求範圍)
private static final DecimalFormat PRICE_FORMAT = new DecimalFormat("#,##0.00");
String s = PRICE_FORMAT.format(value);
// 錯誤
double price = 19.99;
System.out.println(new DecimalFormat("0.00").format(price * 0.1)); // 1.999 → 2.00
// 正確:使用 BigDecimal
BigDecimal price = new BigDecimal("19.99");
BigDecimal discount = price.multiply(new BigDecimal("0.1"));
System.out.println(new DecimalFormat("0.00").format(discount)); // 2.00
NumberFormat compact = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
System.out.println(compact.format(1234)); // 1.2K
System.out.println(compact.format(1234567)); // 1.2M
System.out.println(compact.format(1234567890)); // 1.2B
掌握 DecimalFormat 模式語法、區域化行為、解析邏輯與執行緒安全陷阱,是處理金融、報表、國際化顯示的基本功。建立專案共用的 FormatUtils 統一管理格式化器實例,搭配 BigDecimal 進行精確計算,可避免 90% 的數字顯示與計算錯誤。