我在实际项目中发现现存的代码中对BigDecimal的使用有些可以改进的地方,在此记录下来,供大家参考。

1、new BigDecimal(0)、new BigDecimal("0")、new BigDecimal(1)、new BigDecimal("1")、new BigDecimal(10)、new BigDecimal("10")

BigInteger 和 BigDecimal 这两个高精度数字类,对应整数 0、1、10 的值都有对应的常量(BigInteger.ZERO / BigDecimal.ZERO、BigInteger.ONE / BigDecimal.ONE、BigInteger.TEN / BigDecimal.TEN)可供使用。

为了节省创建对象和垃圾回收的开销,我们应该直接使用这些常量。 尤其是 new BigDecimal("0") 这种用法,它不仅会带来创建 BigDecimal 对象的开销,还有把字符串解析成数字的开销。

ZERO、ONE、TEN 这 3 个常量的值是不会变的,因为跟 Integer、Long 这些实现 Number 接口的类一样,BigInteger 和 BigDecimal 也是不可变的(immutable),也就是说调用一个 BigDecimal 对象的任何公共的成员方法都不会改变该 BigDecimal 对象的值。执行如 BigDecimal.TEN.multiply(new BigDecimal(15)) 这样的代码不会改变 BigDecimal.TEN 的值。

2、BigDecimal.ZERO.subtract(bigDecimal)

取一个 BigDecimal 变量的相反数,我们可以直接使用 BigDecimal 类的 negate 方法。

BigDecimal 类的核心其实是3个私有的成员属性:一个 BigInteger 属性 value 表示数字的各个位、一个整数属性 scale 表示小数点的位置、一个整数属性 sign 表示数字的符号(-1 表示负数、0 表示 0、1 表示正数)。一个 BigDecimal 对象的值就是 sign * value / (10 ^ scale)。例如 value 属性的值为 15、scale 属性的值为 0、sign 属性的值为 -1,则该 BigDecimal 对象表示 -15;如果 value 属性的值为 15、scale 属性的值为 1、sign 属性的值为 1,则该BigDecimal对象表示 15 / 10 = 1.5;如果 BigInteger 属性的值为 15、小数点属性的值为 -1、符号属性的值为 1,则该 BigDecimal 对象表示 15 * 10 = 150。

negate 方法是调用 BigDecimal 的私有构造方法,直接创建一个 sign 属性为原 BigDecimal 对象的 sign 的相反数的新的 BigDecimal 对象,不会涉及到减法运算的逻辑。如果我们使用 BigDecimal.ZERO 的 subtract 方法,则会带来高精度小数减法的开销(BigDecimal 的 subtract 方法需要先判断两个 BigDecimal 对象的 scale 属性的大小,调整其中一个 BigDecimal 对象的 scale 属性和 value 属性使两个 BigDecimal 对象的 scale 属性相等)。

3、bigDecimal.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO.subtract(bigDecimal) : bigDecimal

取一个 BigDecimal 变量的绝对值,我们可以直接使用 BigDecimal 类的 abs 方法。

上文已提到,BigDecimal 类的值由 value、scale 和 sign 3个私有成员属性决定。abs 方法是调用 BigDecimal 的私有构造方法,直接创建一个 sign 属性为 1 的新的 BigDecimal 对象,可以为我们省去判断一个 BigDecimal 对象的值是否小于 0 的开销。

4、bigDecimal.multiply(new BigDecimal(100))、bigDecimal.divide(new BigDecimal(100))

要得到一个 BigDecimal 变量乘以 10 的整数次幂,我们可以直接使用 BigDecimal 类的 scaleByPowerOfTen 方法。

bigDecimal.scaleByPowerOfTen(n) 返回一个值为 bigDecimal * 10 ^ n 的新的 BigDecimal 对象。scaleByPowerOfTen 方法是调用 BigDecimal 的私有构造方法,直接创建一个 scale 属性等于原 BigDecimal 对象的 scale - n 的新的 BigDecimal 对象,可以为我们省去高精度小数乘除法的开销。

要全面深入了解 BigDecimal 的用法,可参考 JavaSE 8 的 API 文档OpenJDK 8 中 BigDecimal.java 的源码