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); // true
3. 金额处理:商业计算的终极方案
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最常用的功能点,建议收藏备用。我在实际项目中就采用类似方案,代码简洁且从不出错。
更多推荐


所有评论(0)