☕ 第六章 Java API
📄 一句话概括

long 装不下的大数交给 BigInteger,double 算不准的钱交给 BigDecimal——构造传字符串、运算调方法、除法带精度,四句口诀走天下。

💡 先修课

第六章 Java API/6.1 包装类(MAX_VALUE 边界 + 拆箱 intValue 的原型)、第六章 Java API/6.2 字符串类(不可变 + 必须接返回值)、2.3 Java中的运算符(/ 整除除不尽的问题)。


1. 两个翻车现场——为什么要"大小数"

现场一:long 也有天花板

long max = Long.MAX_VALUE;
System.out.println(max + 1);   // -9223372036854775808 ??

6.1 印过 int 的边界(21 亿),long 更大(约 922 亿亿)但也有顶——到顶再 +1 会"绕回"最小值(里程表转归零),而且编译器不报错,错误悄悄埋进去。天文数字、大数阶乘(100! 有 158 位)、密码学运算都需要"没有上限的整数"。

现场二:double 算不准

System.out.println(0.1 + 0.2);          // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3);   // false

10. 🔵 计算机基础/02 信息的数字化表示 讲过根子:二进制存不精确十进制小数(0.1 在二进制里是无限循环)。日常算着玩无所谓,但钱不能差 0.0000001——银行、电商计价的场景必须精确。

两扇门:BigInteger(任意大整数)、BigDecimal(精确小数)。

2. 套路一:构造传字符串,别传数字

BigDecimal bd = new BigDecimal("0.1");   // ✓ 按字符串"照原样收",精确
BigDecimal bad = new BigDecimal(0.1);    // ✗ double 先失真,BigDecimal 救不回来
📝 小知识

BigInteger 同理传字符串:new BigInteger("9223372036854775808")——这个数已经超出 long,只能以字符串形式进来。偷懒可以用 BigDecimal.valueOf(0.1),它内部先转字符串再收,效果等同传字符串。

3. 套路二:运算调方法,必须接返回值

它们不支持 + - * /(对象没有运算符重载),四则全靠方法:

BigInteger a = new BigInteger("99999999999999999999");
BigInteger b = new BigInteger("1");

a.add(b);        // 白算!不可变对象,返回新对象没人接
BigInteger c = a.add(b);        // ✓ 接住:100000000000000000000

// BigDecimal 同款四件套
// add 加 / subtract 减 / multiply 乘 / divide 除

和 6.2 String 的铁憨憨一个脾气:不可变 + 方法返回新对象 + 不接返回值等于白调——这三句话在 Java 里会反复出现。

4. 套路三:divide 除不尽会炸

BigDecimal x = new BigDecimal("10");
BigDecimal y = new BigDecimal("3");

x.divide(y);    // ArithmeticException!1/3 是无限小数,存不下

// 正确姿势:指定保留位数 + 舍入模式
BigDecimal z = x.divide(y, 2, RoundingMode.HALF_UP);
System.out.println(z);   // 3.33(HALF_UP = 四舍五入)

常用舍入模式:HALF_UP 四舍五入(最常用)、UP 进位、DOWN 截断、FLOOR 向小取。

5. 套路四:结果转回基本类型

BigDecimal price = new BigDecimal("19.99");
double d = price.doubleValue();    // 19.99
int i = price.intValue();          // 19(砍小数)

intValue()/doubleValue() 就是 6.1 拆箱的同款动作——包装类和大小数都是"对象外壳",转回光杆值是一个路数。

6. 动手环节(IDEA06 · Demo05)

  1. 写 Demo05,打印 Long.MAX_VALUE + 1 和 0.1 + 0.2,亲眼看两个翻车现场。成功标准:负数和 0.3000...4 都出来了。
  2. 30 秒实验 ①:new BigDecimal(0.1) 和 new BigDecimal("0.1") 分别打印 → 对比失真。
  3. 30 秒实验 ②:BigInteger 100 的阶乘(循环 multiply)→ 打印出一个 158 位大数,long 根本装不下。
  4. 30 秒实验 ③:new BigDecimal("10").divide(new BigDecimal("3")) → ArithmeticException;加上 divide(y, 2, RoundingMode.HALF_UP) → 3.33。
  5. 30 秒实验 ④:不接返回值调 a.add(b),打印 a → 原值没变,铁憨憨再上岗。

7. 常见误区

⚠️ 四大误区
  1. new BigDecimal(0.1) 传 double → 把 double 的失真原样继承,必须传字符串或 valueOf。
  2. 以为可以用 + - * / → 编译报错,运算全靠 add/subtract/multiply/divide。
  3. 不接返回值 → 不可变对象,运算结果白算(String/6.x 系列同款坑)。
  4. divide 不给舍入模式 → 除不尽直接炸 ArithmeticException。

8. 本节小结

要点 内容
BigInteger 任意大整数:long 溢出的解药
BigDecimal 精确小数:double 失真的解药,金额标配
构造 传字符串(或 valueOf),别传 double
运算 add/subtract/multiply/divide,方法代替运算符,必须接返回值
divide 除不尽必须给保留位数 + RoundingMode(HALF_UP 四舍五入)
转回 intValue()/doubleValue(),6.1 拆箱同款

9. 可折叠自测题

❓ Q1:为什么 `new BigDecimal(0.1)` 不精确而 `new BigDecimal("0.1")` 精确?

0.1 作为 double 已经失真(二进制存不精确十进制小数),BigDecimal 只是把失真的值原样收下;字符串构造则跳过 double,按字面"0.1"精确记录。偷懒写法 BigDecimal.valueOf(0.1) 内部走字符串,等效。

❓ Q2:BigInteger a、b,为什么 `a + b` 编译报错?正确写法?

对象没有四则运算符重载,Java 的 + - * / 只给基本类型用。正确写法 a.add(b),且要接返回值 BigInteger c = a.add(b);。

❓ Q3:`new BigDecimal("10").divide(new BigDecimal("3"))` 抛什么异常?怎么救?

ArithmeticException(1/3 无限小数存不下)。救法:divide(y, 2, RoundingMode.HALF_UP) 保留 2 位四舍五入。

❓ Q4:算完总价想显示成 double,用什么方法?这个方法在 6.1 里叫什么?

doubleValue()——和 6.1 Integer 拆箱的 intValue()/doubleValue() 同款动作,都是"对象外壳 → 光杆值"。

❓ Q5(毕业题):电商购物车:单价 19.99 数量 3,用 BigDecimal 算总价并打印。
BigDecimal price = new BigDecimal("19.99");
BigDecimal count = new BigDecimal("3");
BigDecimal total = price.multiply(count);       // 59.97
System.out.println("总价:" + total + " 元");

乘法除不除得尽都精确;若再打 8.8 折(除以……或乘 0.88)记得同样用字符串构造,遇到除法带上 RoundingMode。


10. 下一步学什么


相关笔记