1. 概述
本文将系统介绍Java中数字格式化的多种实现方式,涵盖基础格式化、四舍五入处理、特殊类型格式化及高级应用场景。每种方法都配有实际代码示例,助你快速掌握数字格式化的核心技巧。
2. 基础格式化:String.format()
String.format()
是最简单直接的数字格式化方法,通过格式化字符串控制小数位数:
double value = 4.2352989244d;
assertThat(String.format("%.2f", value)).isEqualTo("4.24");
assertThat(String.format("%.3f", value)).isEqualTo("4.235");
✅ 核心要点:
- 第一个参数定义格式模式(如
%.2f
表示保留2位小数) - 第二个参数传入待格式化的数值
- 返回格式化后的字符串
3. 四舍五入格式化
Java中处理小数主要有两种基本类型:float
和double
:
double myDouble = 7.8723d;
float myFloat = 7.8723f;
实际开发中通常只需保留几位小数,以下是两种常用四舍五入方案:
3.1. 使用BigDecimal实现精确控制
BigDecimal
提供精确的舍入控制,推荐用于金融计算等场景:
public static double withBigDecimal(double value, int places) {
BigDecimal bigDecimal = new BigDecimal(value);
bigDecimal = bigDecimal.setScale(places, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}
使用示例:
double D = 4.2352989244d;
assertThat(withBigDecimal(D, 2)).isEqualTo(4.24);
assertThat(withBigDecimal(D, 3)).isEqualTo(4.235);
⚠️ 关键操作:
setScale()
指定小数位数和舍入模式RoundingMode.HALF_UP
即"四舍五入"
3.2. 使用Math.round()简单实现
通过数学运算实现快速舍入(但存在精度陷阱):
public static double withMathRound(double value, int places) {
double scale = Math.pow(10, places);
return Math.round(value * scale) / scale;
}
使用示例:
assertThat(withMathRound(D, 2)).isEqualTo(4.24);
assertThat(withMathRound(D, 3)).isEqualTo(4.235);
❌ 踩坑警告:
System.out.println(withMathRound(1000.0d, 17)); // 输出92.23372036854776 !!
System.out.println(withMathRound(260.775d, 2)); // 输出260.77而非260.78
👉 原因:Math.round()
存在截断问题,仅建议学习使用,生产环境慎用!
4. 特殊类型格式化
4.1. 大整数加逗号分隔
使用DecimalFormat
格式化大整数,添加千位分隔符:
public static String withLargeIntegers(double value) {
DecimalFormat df = new DecimalFormat("###,###,###");
return df.format(value);
}
int value = 123456789;
assertThat(withLargeIntegers(value)).isEqualTo("123,456,789");
4.2. 数字补零
固定长度数字补零(如生成订单号):
public static String byPaddingZeros(int value, int paddingLength) {
return String.format("%0" + paddingLength + "d", value);
}
int value = 1;
assertThat(byPaddingOutZeros(value, 3)).isEqualTo("001");
4.3. 强制保留两位小数
确保数字始终显示两位小数:
public static double withTwoDecimalPlaces(double value) {
DecimalFormat df = new DecimalFormat("#.00");
return new Double(df.format(value));
}
int value = 12;
assertThat(withTwoDecimalPlaces(value)).isEqualTo(12.00);
4.4. 百分比格式化
本地化百分比显示:
public static String forPercentages(double value, Locale locale) {
NumberFormat nf = NumberFormat.getPercentInstance(locale);
return nf.format(value);
}
double value = 25f / 100f;
assertThat(forPercentages(value, new Locale("en", "US"))).isEqualTo("25%");
4.5. 货币格式化
根据地区自动显示货币符号:
public static String currencyWithChosenLocalisation(double value, Locale locale) {
NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
return nf.format(value);
}
多地区示例:
double value = 23_500;
assertThat(currencyWithChosenLocalisation(value, new Locale("en", "US"))).isEqualTo("$23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("zh", "CN"))).isEqualTo("¥23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("pl", "PL"))).isEqualTo("23 500,00 zł");
4.6. 科学计数法格式化
超大/超小数字的科学计数法表示:
public static String formatScientificNotation(double value, Locale localisation) {
return String.format(localisation, "%.3E", value);
}
Locale us = new Locale("en", "US");
assertThat(formatScientificNotation(3.14159, us)).isEqualTo("3.142E+00");
assertThat(formatScientificNotation(0.0123456, us)).isEqualTo("1.235E-02");
assertThat(formatScientificNotation(1111111, us)).isEqualTo("1.111E+06");
固定宽度格式化(不足补空格):
public static String formatScientificNotationWithMinChars(double value, Locale localisation) {
return String.format(localisation, "%12.4E", value);
}
assertThat(formatScientificNotationWithMinChars(3.14159, us)).isEqualTo(" 3.1416E+00");
5. 高级格式化技巧
DecimalFormat
的本地化处理:
public static double withDecimalFormatLocal(double value) {
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.getDefault());
return new Double(df.format(value));
}
自定义模式格式化:
public static double withDecimalFormatPattern(double value, int places) {
DecimalFormat df2 = new DecimalFormat("#,###,###,##0.00");
DecimalFormat df3 = new DecimalFormat("#,###,###,##0.000");
if (places == 2)
return new Double(df2.format(value));
else if (places == 3)
return new Double(df3.format(value));
else
throw new IllegalArgumentException();
}
assertThat(withDecimalFormatPattern(D, 2)).isEqualTo(4.24);
assertThat(withDecimalFormatPattern(D, 3)).isEqualTo(4.235);
6. 总结
Java数字格式化方案对比:
场景 | 推荐方案 | 特点 |
---|---|---|
基础格式化 | String.format() |
简单直接,适合快速实现 |
精确舍入 | BigDecimal |
金融级精度,可控性强 |
大整数显示 | DecimalFormat |
自动添加千位分隔符 |
货币/百分比 | NumberFormat |
本地化支持,自动适配地区 |
科学计数法 | String.format() |
灵活控制精度和宽度 |
💡 最佳实践:
- 金融计算优先使用
BigDecimal
- 需要本地化时选择
NumberFormat
- 简单场景用
String.format()
快速实现
所有示例代码已上传至GitHub仓库,欢迎参考实践。