我的其他文章也讲解的比较有趣,如果喜欢博主的讲解方式,可以多多支持一下,感谢!
了解 MySQL 日志文件 : MySQL 日志文件:数据库的“黑匣子”
其他优质专栏: 【SpringBoot】【多线程】【Redis】【✨设计模式专栏(已完结)】…等
如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning
这篇文章让我来详细讲解 Java 中的 BigDecimal 类,包括它的用途、适用场景、以及潜在问题。
BigDecimal
是 Java 中用于表示任意精度的十进制数的类。它解决了 float
和 double
在进行精确计算时可能出现的精度丢失问题。BigDecimal
类提供了对数字进行加、减、乘、除等运算的方法,并且可以控制舍入模式。
float
和 double
类型是基于二进制的浮点数表示,它们无法精确地表示某些十进制数(例如 0.1)。这会导致在进行涉及货币、金融等需要精确计算的场景时出现误差。
例如:
double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println(sum); // 输出:0.30000000000000004
可以看到,0.1 + 0.2
的结果并不是精确的 0.3
。 而BigDecimal
可以避免这种问题。✅
BigDecimal
的用法
创建 BigDecimal
对象:
BigDecimal(String val)
:推荐使用字符串构造方法,避免 double
带来的精度问题。 BigDecimal(int val)
BigDecimal(long val)
BigDecimal(double val)
:不推荐,因为 double
本身就可能存在精度问题。 BigDecimal.valueOf(double val)
:底层也是将 double
转换为字符串,然后创建 BigDecimal
对象,比直接使用 BigDecimal(double)
更好。 BigDecimal num1 = new BigDecimal("0.1"); // 推荐
BigDecimal num2 = new BigDecimal(0.1); // 不推荐
BigDecimal num3 = BigDecimal.valueOf(0.1); // 推荐
常用方法:
add(BigDecimal augend)
:加法subtract(BigDecimal subtrahend)
:减法multiply(BigDecimal multiplicand)
:乘法divide(BigDecimal divisor)
:除法divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
:除法,指定精度和舍入模式。setScale(int newScale, RoundingMode roundingMode)
:设置精度和舍入模式。compareTo(BigDecimal val)
:比较大小(返回 -1, 0, 1,分别表示小于、等于、大于)toString()
:转换为字符串intValue()
, longValue()
, floatValue()
, doubleValue()
:转换为基本数据类型(可能丢失精度) ⚠️舍入模式(RoundingMode
):
RoundingMode.UP
:远离零方向舍入。RoundingMode.DOWN
:趋向零方向舍入。RoundingMode.CEILING
:趋向正无穷方向舍入。RoundingMode.FLOOR
:趋向负无穷方向舍入。RoundingMode.HALF_UP
:四舍五入(大于等于 0.5 进位)。RoundingMode.HALF_DOWN
:五舍六入(大于 0.5 进位)。RoundingMode.HALF_EVEN
:银行家舍入法(四舍六入,五看前一位,偶数舍去,奇数进位)。RoundingMode.UNNECESSARY
:断言不需要舍入,如果需要舍入则抛出 ArithmeticException
。示例代码
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalExample {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("10.5");
BigDecimal num2 = new BigDecimal("3.2");
// 加法
BigDecimal sum = num1.add(num2);
System.out.println("Sum: " + sum);
// 减法
BigDecimal difference = num1.subtract(num2);
System.out.println("Difference: " + difference);
// 乘法
BigDecimal product = num1.multiply(num2);
System.out.println("Product: " + product);
// 除法 (需要指定精度和舍入模式)
try {
BigDecimal quotient = num1.divide(num2, 2, RoundingMode.HALF_UP); // 保留两位小数,四舍五入
System.out.println("Quotient: " + quotient);
} catch (ArithmeticException e) {
System.out.println("除法运算出错:" + e.getMessage());
}
// 设置精度
BigDecimal scaledNum = num1.setScale(1, RoundingMode.HALF_UP); // 保留一位小数,四舍五入
System.out.println("Scaled Number: " + scaledNum);
// 比较大小
int comparisonResult = num1.compareTo(num2);
if (comparisonResult > 0) {
System.out.println("num1 is greater than num2");
} else if (comparisonResult < 0) {
System.out.println("num1 is less than num2");
} else {
System.out.println("num1 is equal to num2");
}
}
}
性能问题: BigDecimal
的运算比 float
和 double
慢得多。 因为它需要进行复杂的计算来保证精度。 因此,只有在需要精确计算的场景下才应该使用 BigDecimal
。
不可变性: BigDecimal
对象是不可变的。 这意味着每次运算都会创建一个新的 BigDecimal
对象。 因此,在循环中进行大量的 BigDecimal
运算可能会导致性能问题和内存占用过高。 ⚠️
BigDecimal total = BigDecimal.ZERO;
for (int i = 0; i < 1000; i++) {
total = total.add(new BigDecimal("0.1")); // 每次循环都创建新的 BigDecimal 对象
}
System.out.println(total);
如果需要进行大量的 BigDecimal
运算,可以考虑使用 BigDecimal
的 MathContext
来控制精度和舍入模式,或者使用其他优化技巧。
NullPointerException
: BigDecimal
对象可以为 null
。 在使用 BigDecimal
对象之前,应该先进行判空检查,避免 NullPointerException
。
除法运算: 在进行除法运算时,必须指定精度和舍入模式,否则可能会抛出 ArithmeticException
(如果结果是无限循环小数)。 ➗
构造方法选择: 始终使用 BigDecimal(String)
或 BigDecimal.valueOf(double)
构造方法,避免 double
带来的精度问题。
比较大小: 使用 compareTo()
方法进行比较,不要使用 ==
。 ==
比较的是对象的引用,而不是值。 ⚖️
缓存 BigDecimal 对象: 对于一些常用的 BigDecimal 值(例如 0, 1, 10),可以考虑使用静态常量来缓存这些对象,避免重复创建。 ♻️
public static final BigDecimal ZERO = BigDecimal.valueOf(0);
public static final BigDecimal ONE = BigDecimal.valueOf(1);
public static final BigDecimal TEN = BigDecimal.valueOf(10);
BigDecimal
是 Java 中用于进行精确十进制计算的重要类。 它适用于金融、科学等需要高精度计算的场景。 但是,BigDecimal
的运算速度较慢,并且需要注意一些潜在的问题,例如精度、舍入模式、NullPointerException
等。 在选择使用 BigDecimal
时,需要权衡精度和性能,并根据实际情况选择合适的构造方法、运算方法和舍入模式。
希望这篇文章能够帮助你更好地理解和使用 BigDecimal
类。