JAVA中如何处理金额类数据

由闲鱼的转账BUG引起的思考

想写这一篇文章主要是前两天看到了一个闲鱼的BUG,而且自己也复现了这个问题。在使用闲鱼的时候当我向好友转账2.1的时候,最终支付宝显示的却只有2.09(前天出现的问题,目前闲鱼已经修复了这个问题)。作为一个消费者这只是一个BUG,但是作为一个JAVA开发,就让我思考到假如这个金额的数据是需要服务端进行处理?JAVA要如何处理这些数据。

在这里插入图片描述

我的一分钱呢??????在这里插入图片描述

在这里插入图片描述


这从未设想的问题啊,所以这里就整理下对于服务端 的开发,对于金额的处理、储存和传输应该如何操作

JAVA对金额数据的处理

错误的付款金额

在设计商品的数据结构时候,我们可能尝试将商品的价格设置为float或者double类型,而购买数量因为产品不同可能被设计为int或者long。当需要我们计算总价的时候,如果我们直接将单价*购买数量就会出现下面的情况:

    public static void main(String[] args) {
        float a = 72.49f;
        System.out.println("商品a单价:" + a);
        int n = 10;
        System.out.println("购买a数量:" + n);
        System.out.println("商品a总价:" + a*n);

        double d1 = 0.58D;
        long n1 = 100L;
        System.out.println("商品b单价:" + d1);
        System.out.println("购买b数量:" + n1);
        System.out.println("商品b总价:" + d1*n1);
    }

上面代码看起来就是很简单的乘法运算,但是投入到生产中会出现很大的问题,它会得到下面的结果

商品a单价:72.49
购买a数量:10
商品a总价:724.89996
商品b单价:0.58
购买b数量:100
商品b总价:57.99999999999999

可以看到本来应该支付724.9元的订单只需要支付724.89。而本来需要58块的订单缺只需要57.99。无论是float还是double都出现了金额缺失的情况。

使用BigDecimal进行金额计算

因为float和double存在精度丢失问题所以在进行数字的精确计算的时候,我们需要通过BigDecmal来进行精确计算。

将数字转换为BigDecimal

BigDecimal提供了相当多的构造方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NeOuTEcX-1593868618419)(BCC0E85C61C04C38A0C7D2A4C3AD8F5F)]

上面方法虽然多但是我们常用的构造方法就是下面几种,通过下面的方法来将String、int和long类型的数据转换为BigDecimal

    // BigDecimal(int) 创建一个具有参数所指定整数值的对象
    BigDecimal num1 = new BigDecimal(10);
    // BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
    BigDecimal num2 = new BigDecimal(1000000L);
    //BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象
    BigDecimal num12 = new BigDecimal("0.005");

对于double和float的特殊处理

如果我们将double或者float数据使用上面方式获取BigDecimal则会得到下面这种错误结果

// 输出:72.48999786376953125
        float a = 72.49f;
        BigDecimal num3 = new BigDecimal(a);
        System.out.println(num3);
// 输出:0.57999999999999996003197111349436454474925994873046875
        double d1 = 0.58D;
        BigDecimal num4 = new BigDecimal(d1);
        System.out.println(num4);

有些文章中介绍可以使用其静态方法BigDecimal.valueOf(d1),但是此方法面对float的数据类型依旧无法准确输出内容。所以对于float我们最好将其转换为String后进行处理

        float a = 72.49f;
        String s = String.valueOf(a);
        System.out.println(s);
对BigDecimal 数据进行操作

BigDecimal提供了一系列的方法让我们更加精确的对数据进行处理

方法作用例子解释
add加法num1.add(num1和num2相加)num1和num2相加
subtract减法num1.subtract(num2)num1减去num2
multiply乘法num1.multiply(num2)num1乘 num2
divide除法num2.divide(num1,2,BigDecimal.ROUND_HALF_UP)num2 除以 num1,并且保留两位小数
divideToIntegralValue除法并获取其整数部分num2.divideToIntegralValue(num1)num2 除以 num1,并获取其整数部分
compareTo比较大小num1.compareTo(num2)num1和num2比大小,如果num1小于num2则返回-1,相等则返回0,大于则返回1
abs绝对值num3.abs()返回num3的绝对值

特别需要注意!进行相关操作后并不会作用到原始数据上

在BigDecimal数据进行上面操作后并不会影响其原始数据的值,下面的操作中最终会存在三个不一样的值,原始的数据bigDecimal1bigDecimal2计算后的结果add

        BigDecimal add = bigDecimal1.add(bigDecimal2);
        System.out.println(add);
        System.out.println(bigDecimal1);
        System.out.println(bigDecimal2);

除法四舍五入操作

除法操作时调用的方法

public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode);
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode);

其最后一个参数用来确定小数的舍入的策略。

数字参数枚举参数作用
BigDecimal.ROUND_UPRoundingMode.UP被舍弃的小数位如果不是0,则舍弃部分前面的数字+1
BigDecimal.ROUND_DOWNRoundingMode.DOWN不会对舍弃部分前面的数字+1
BigDecimal.ROUND_CEILINGRoundingMode.CEILING如果结果是正数则使用RoundingMode.UP规则;如果结果是负数则使用RoundingMode.DOWN规则
BigDecimal.ROUND_FLOORRoundingMode.FLOOR使用和RoundingMode.CEILING相反的策略
BigDecimal.ROUND_HALF_UPRoundingMode.HALF_UP可以理解为四舍五入
BigDecimal.ROUND_HALF_DOWNRoundingMode.HALF_DOWN舍弃部分 > 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同RoundingMode.DOWN
BigDecimal.ROUND_HALF_EVENRoundingMode.HALF_EVEN如果距离相邻的数字相等,则向相邻的偶数舍入,如果不相等,则如果舍弃部分左边的数字为奇数,则舍入行为同RoundingMode.HALF_UP;如果为偶数,则舍入行为同RoundingMode.HALF_DOWN
BigDecimal.ROUND_UNNECESSARYRoundingMode.UNNECESSARY判断是否精确操作,如果需要进行舍入操作则抛出异常

金额类数据如何保存(MySQL)

在金额类数据处理完后,我们需要保存到数据库中,而对于这些交易数据,根据每个系统涉及交易的规模和业务不同,目前有三种选择(实际上只写了两种,网上有人介绍使用String或者说varchar,说实话我是不喜欢将金额存储为字符串)。

decimal

使用decimal在数据库中可以非常精确的表示一个数据的值,而一般保存交易金额我们可以将其类型设置为decimal(M,S),M表示整数和小数部分的总长度,S表示其中小数部分的位数,对于日常交易过程中我们所使用的的最小单位是分,也就是0.01元,所以可以设置为2;

long

有些设计中,将数据库中金额的单位认为是。对于这种设计对于金额的数据类型可以设置为long,此时对于值为100的数据,会被认定为1元,而不是100元。

金额类数据如何传输

关于在通过跨服务跨系统进行金额数据传输的时候,数据类型如何确定,可以直接参照支付宝SDK上的要求使用String数据类型


个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容。假如我的这篇内容对你有任何帮助的话,麻烦给我点一个赞。你的点赞就是我前进的动力。

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java保险项目是一种基于Java语言开发的保险管理系统,旨在提供一个全面、高效、安全的保险业务处理平台。该项目涵盖了保险产品管理、保单管理、理赔管理等功能,旨在帮助保险公司提高运营效率和客户满意度。 首先,保险产品管理是该项目的核心功能之一。通过该功能,保险公司可以定义和配置各种保险产品,包括保费计算公式、保险责任、保额范围等。同时,可以设置产品的上线、下线时间,以及不同产品的销售渠道,实现灵活的产品管理和销售。 其次,保单管理是该项目的重要组成部分。保险公司可以通过该功能实现保单的录入、修改、查询和删除等操作。同时,系统还提供了保单状态的管理,如保单生效、保单失效等,方便保险公司掌握保单的实时状态。 另外,该项目还包含了理赔管理功能。保险公司可以通过该功能实现理赔案件的申请、审核、支付等流程。系统会根据保险产品的规定自动计算理赔金额,并跟踪理赔进度,提供实时的数据统计和查询功能,帮助保险公司提高理赔效率。 此外,该项目还具备其他功能,如保险合同管理、代理人管理、客户管理等,以及与其他系统的数据对接功能,实现了信息的共享和交流。 总之,Java保险项目是一种功能全面、高度可定制的保险管理系统,能够帮助保险公司实现业务的自动化和信息化,提高运营效率,保证数据的准确性和安全性,提升客户满意度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值