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最常用的功能点,建议收藏备用。我在实际项目中就采用类似方案,代码简洁且从不出错。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐