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)
- 写
Demo05,打印Long.MAX_VALUE + 1和0.1 + 0.2,亲眼看两个翻车现场。成功标准:负数和 0.3000...4 都出来了。 - 30 秒实验 ①:
new BigDecimal(0.1)和new BigDecimal("0.1")分别打印 → 对比失真。 - 30 秒实验 ②:BigInteger 100 的阶乘(循环 multiply)→ 打印出一个 158 位大数,long 根本装不下。
- 30 秒实验 ③:
new BigDecimal("10").divide(new BigDecimal("3"))→ ArithmeticException;加上divide(y, 2, RoundingMode.HALF_UP)→ 3.33。 - 30 秒实验 ④:不接返回值调
a.add(b),打印 a → 原值没变,铁憨憨再上岗。
7. 常见误区
- new BigDecimal(0.1) 传 double → 把 double 的失真原样继承,必须传字符串或 valueOf。
- 以为可以用 + - * / → 编译报错,运算全靠 add/subtract/multiply/divide。
- 不接返回值 → 不可变对象,运算结果白算(String/6.x 系列同款坑)。
- divide 不给舍入模式 → 除不尽直接炸 ArithmeticException。
8. 本节小结
| 要点 | 内容 |
|---|---|
| BigInteger | 任意大整数:long 溢出的解药 |
| BigDecimal | 精确小数:double 失真的解药,金额标配 |
| 构造 | 传字符串(或 valueOf),别传 double |
| 运算 | add/subtract/multiply/divide,方法代替运算符,必须接返回值 |
| divide | 除不尽必须给保留位数 + RoundingMode(HALF_UP 四舍五入) |
| 转回 | intValue()/doubleValue(),6.1 拆箱同款 |
9. 可折叠自测题
0.1 作为 double 已经失真(二进制存不精确十进制小数),BigDecimal 只是把失真的值原样收下;字符串构造则跳过 double,按字面"0.1"精确记录。偷懒写法 BigDecimal.valueOf(0.1) 内部走字符串,等效。
对象没有四则运算符重载,Java 的 + - * / 只给基本类型用。正确写法 a.add(b),且要接返回值 BigInteger c = a.add(b);。
ArithmeticException(1/3 无限小数存不下)。救法:divide(y, 2, RoundingMode.HALF_UP) 保留 2 位四舍五入。
doubleValue()——和 6.1 Integer 拆箱的 intValue()/doubleValue() 同款动作,都是"对象外壳 → 光杆值"。
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. 下一步学什么
- ☑ 6.6 日期和时间类 ✅ —— 已完成:点此复习 —— 6.3 秒表里那串毫秒值的"人话翻译"正式登场:Date + SimpleDateFormat 两级翻译,外加 JDK8 新日期类打照面。
相关笔记
- 第六章 Java API/6.4 Math类和Random类 ← 上一篇:同为算术域工具,本节管"精确"
- 第六章 Java API/6.1 包装类:MAX_VALUE 边界 + intValue 拆箱的原型
- 第六章 Java API/6.2 字符串类:不可变 + 接返回值的三连规矩
- 10. 🔵 计算机基础/02 信息的数字化表示:0.1 失真的二进制根子