Hutool NumberUtil 实战:从基础运算到高级数值处理的完整指南
1. 为什么你需要Hutool NumberUtil?
做后端开发的朋友们应该都遇到过这样的场景:电商订单金额计算时出现几分钱的误差,报表数据展示时小数位乱七八糟,优惠券码生成需要不重复的随机数,ID转换需要处理不同进制...这些看似简单的数值处理,实际写起来却处处是坑。
我刚开始做电商系统时就踩过不少坑。比如用Java原生的double做金额计算,0.1+0.2居然等于0.30000000000000004;用Math.random()生成优惠码,结果出现了重复;自己写BigDecimal四舍五入,代码又臭又长...直到发现了Hutool的NumberUtil,这些问题都迎刃而解。
NumberUtil最大的价值在于:
- 精度无忧:自动用BigDecimal处理运算,告别float/double的精度问题
- 开箱即用:一行代码搞定随机数、进制转换等复杂操作
- 业务友好:金额格式化、百分比展示等场景都有现成方案
- 性能可靠:底层经过优化,比我们自己写的工具类更高效
2. 基础运算:告别精度丢失的噩梦
2.1 四则运算的正确打开方式
先看一个经典问题:
System.out.println(0.1 + 0.2); // 输出0.30000000000000004商业计算中这种精度问题绝对不能忍。NumberUtil的解决方案很优雅:
// 加法 NumberUtil.add(0.1, 0.2); // 0.3 // 减法 NumberUtil.sub(1.5, 0.3); // 1.2 // 乘法 NumberUtil.mul(2.5, 4); // 10.0 // 除法(自动保留10位小数) NumberUtil.div(10, 3); // 3.3333333333 // 指定精度除法(保留2位,四舍五入) NumberUtil.div(10, 3, 2); // 3.33实际项目中我常用这些方法处理:
- 订单金额计算(加减乘除)
- 折扣率计算(除法)
- 税费计算(乘法)
2.2 比较运算的坑你踩过吗?
比较两个数字时,直接使用==可能会出问题:
Double a = 0.1; Double b = 0.1; System.out.println(a == b); // 有时true有时false!NumberUtil的比较方法更可靠:
NumberUtil.compare(0.1, 0.2); // -1 (小于) NumberUtil.equals(0.1, 0.1); // true3. 金额处理:商业计算的终极方案
3.1 保留小数的两种姿势
电商系统最常遇到的场景:金额需要保留2位小数。NumberUtil提供两种方式:
方法1:round(更灵活)
double price = 99.9876; NumberUtil.round(price, 2); // 99.99 (四舍五入) NumberUtil.round(price, 2, RoundingMode.DOWN); // 99.98 (直接截断)方法2:roundStr(更简单)
NumberUtil.roundStr(99.9876, 2); // "99.99"个人经验:报表展示用roundStr,需要继续计算的用round。
3.2 金额格式化的艺术
不同地区金额格式不同:
// 中文格式 NumberUtil.decimalFormat("¥#,###.##", 1234567.89); // "¥1,234,567.89" // 欧美格式 NumberUtil.decimalFormat("$#,##0.00", 1234567.89); // "$1,234,567.89" // 带千分位的科学计数法 NumberUtil.decimalFormat("#.###E0", 1234567.89); // "1.235E6"电商系统常用技巧:
// 商品价格显示 String format = NumberUtil.decimalFormat("¥#.##", 99.5); // "¥99.5" // 大额金额显示 NumberUtil.decimalFormat("¥#,###万", 123456789); // "¥12,345万"4. 随机数与ID生成实战
4.1 生成不重复随机数
优惠券码生成是个典型场景:
// 生成10个0-100的不重复随机数 int[] coupons = NumberUtil.generateRandomNumber(0, 100, 10); // 生成20个100-200的不重复随机数(返回Integer[]) Integer[] vipCodes = NumberUtil.generateBySet(100, 200, 20);实际项目中我这样用:
// 生成6位验证码 int[] codes = NumberUtil.generateRandomNumber(100000, 999999, 1); // 生成优惠券批次号 Integer[] batchNos = NumberUtil.generateBySet(1000, 9999, 50);4.2 进制转换的妙用
处理不同系统的ID转换:
// 十进制转二进制 NumberUtil.getBinaryStr(8); // "1000" // 二进制转十进制 NumberUtil.binaryToInt("1111"); // 15 // 十六进制处理 NumberUtil.hexToInt("FF"); // 255实际应用案例:
// 短网址生成(10进制转36进制) Long.toString(123456789L, 36); // "21i3v9" // 商品编码转换 NumberUtil.getBinaryStr(productId); // 生成二进制编码5. 高级数值处理技巧
5.1 数学运算不再头疼
开发中常用的数学运算:
// 阶乘(5! = 120) NumberUtil.factorial(5); // 平方根 NumberUtil.sqrt(16); // 4.0 // 最大公约数 NumberUtil.divisor(15, 25); // 5 // 最小公倍数 NumberUtil.multiple(15, 25); // 75真实业务场景:
// 计算商品包装组合(利用最大公约数) int boxSize = NumberUtil.divisor(12, 18); // 6个/箱 // 计算促销周期(最小公倍数) int promotionDays = NumberUtil.multiple(3, 5); // 15天循环5.2 数字校验的必备技能
表单校验常用方法:
// 是否为数字 NumberUtil.isNumber("123.45"); // true // 是否为整数 NumberUtil.isInteger("123.00"); // false // 是否为质数 NumberUtil.isPrimes(17); // true // 去除多余0 NumberUtil.toStr(99.000); // "99"实际使用建议:
// 价格输入校验 if(!NumberUtil.isNumber(input)) { throw new IllegalArgumentException("请输入有效金额"); } // ID格式校验 if(NumberUtil.isInteger(idStr)) { id = Long.parseLong(idStr); }6. 实战:电商订单计算全流程
假设我们要实现一个订单金额计算模块:
// 1. 计算商品总价(单价×数量) double total = NumberUtil.mul(price, quantity); // 2. 计算折扣后金额(保留2位小数) double discounted = NumberUtil.div(total, discountRate, 2); // 3. 计算税费(四舍五入) double tax = NumberUtil.round(NumberUtil.mul(discounted, taxRate), 2); // 4. 最终金额 double finalAmount = NumberUtil.add(discounted, tax); // 5. 生成订单编号(日期+随机数) String orderNo = DateUtil.today() + NumberUtil.generateRandomNumber(1000, 9999, 1)[0]; // 6. 金额格式化展示 String displayAmount = NumberUtil.decimalFormat("¥#,##0.00", finalAmount);这个流程涵盖了NumberUtil最常用的功能点,建议收藏备用。我在实际项目中就采用类似方案,代码简洁且从不出错。
