天天看点

java BigDecimal中的坑们

起初接触这个类就觉得这个类很神奇,各种方法的都不会改变其本身,这也容易引起很多BUG。

比如 BigDecimal.setScale()这个方法,正常理解调用这个方法以后原来的对象的精度就被改变了,但事实并非如此:

public void test5() {
        BigDecimal decimal = BigDecimal.valueOf(3.333333333);
        System.out.println(decimal);
        decimal.setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println(decimal);
    }
           

输出结果是  

java BigDecimal中的坑们

这不是没变化吗?

java BigDecimal中的坑们

后来发现IDEA的小提示:

java BigDecimal中的坑们

他说我把这个方法的返回值忽略了,我看了一下还真是,一般POJO的set方法都是无返回值的,这玩意特立独行有个BigDecimal的返回值。然后我们把代码改成这样:

public void test5() {
        BigDecimal decimal = BigDecimal.valueOf(3.333333333);
        System.out.println(decimal);
        BigDecimal decimal2 = decimal.setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println(decimal2);
    }
           

输出结果:

java BigDecimal中的坑们

果然就可以了。

BigDecimal果然是一个奇葩的类,天真的我以为事情到此就结束了,直到有一天,写的功能又出问题了

事情大概是这样的:除了改变了它的精度以外,我还在别的方法里面改了它的值:

public void test5() {
        BigDecimal decimal = BigDecimal.valueOf(3.333333333);
        System.out.println(decimal);
        BigDecimal decimal2 = decimal.setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println(decimal2);
        change(decimal);
        System.out.println(decimal);
    }

    private void change(BigDecimal bigDecimal) {
        bigDecimal = bigDecimal.multiply(BigDecimal.TEN);
    }
           

输出结果:

java BigDecimal中的坑们

这不是又没变吗?

java BigDecimal中的坑们

根据我们以往的概念,调用方法的时候参数为非基本数据类型的都是引用传递,方法里面改了它的值方法外面跟着动,因为它就是同一个实例。但这个BigDecimal好像就是不符合常理。后来查了一下,这个BigDecimal居然是个不可变类。有同学不太理解不可变类是啥?这么说吧,你可以把它理解为它的所有属性就是final的,不可改变。所以你调用它的任何方法都不会改变实例本身,而是创建一个新的对象。就跟String一样。

java BigDecimal中的坑们

这样一来都能想的明白了,原来setScale方法也是这个解释。但是为啥我在别的方法里面改这个对象改不了了呢?

我们这样理解一下:

public void test6() {
        String a = new String("ori");
        BigDecimal b = BigDecimal.TEN;
        System.out.println(a);
        System.out.println(b);
    }

    private void change(String c, BigDecimal d) {
        c = new String("hhh");
        d = BigDecimal.ZERO;
    }
           

结果:

java BigDecimal中的坑们

在调用change方法的时候,b对象的确是传了进去,被形参d指向,这个时候b和d都指向同一个对象,然后我在方法内部把d指向了一个新的对象,但是原来b的指向没变,所以b始终没变。

综上,我们得出如下结论:

1.对不可变类的任何操作都不会改变不可变类本身实例,而是创造一个新的实例(原来不用的实例会在GC时被回收)

2.不可变类作为参数传入方法内,方法内对不可变类的操作不会对方法外部造成影响。