金融级java开发中货币字段必须用bigdecimal且仅通过字符串构造,推荐以“分”为单位用long存储,所有运算需显式指定精度和roundingmode,字段命名须体现单位并配校验。

在金融级Java开发中,货币字段绝不能用double或float——这是最核心、最不可妥协的规范。浮点数的精度丢失会导致金额计算错误,哪怕万分之一的偏差,在清算、对账、计息等场景下都可能引发合规风险甚至资损。
必须使用BigDecimal,且严格限定构造方式
BigDecimal是唯一被广泛接受的货币数值类型,但关键在于“怎么创建”。以下做法必须禁止:
- ❌
new BigDecimal(19.99)—— 传入double字面量,会继承其二进制表示误差(如19.99实际存为19.989999999999998...) - ❌
BigDecimal.valueOf(double)—— 本质仍是基于double,仅对“能精确表示的double值”安全(如1.0、2.5),但业务中无法保障输入来源
✅ 正确做法只有一种:始终使用字符串构造:
new BigDecimal("19.99") 或 BigDecimal.valueOf(Long.parseLong("1999")).divide(BigDecimal.valueOf(100))(适用于分单位存储)
统一货币单位:推荐以“分”为最小单位,用long或Integer
对高并发、高频计算(如支付扣减、实时风控)场景,BigDecimal对象创建和运算开销明显。更稳健的金融实践是:
- 数据库字段定义为
BIGINT或INT,存储“分”(即原始金额 × 100),不带小数点 - Java实体类中对应字段声明为
Long(推荐)或Integer(需确保≤21亿分,即≤21,000,000.00元) - 所有加减乘除均用整数运算,零误差、零GC、高性能
- 展示层/DTO中再按需转为“元”,格式化为两位小数(如
String.format("%.2f", amount / 100.0))
精度与舍入规则必须显式声明,禁止依赖默认
一旦使用BigDecimal,所有除法、setScale等操作必须明确指定RoundingMode:
- ❌
amount.divide(new BigDecimal("3"))—— 抛ArithmeticException(无限小数) - ❌
amount.setScale(2)—— 使用默认RoundingMode.HALF_UP,但未显式声明,可读性差,易被误改 - ✅
amount.divide(new BigDecimal("3"), 2, RoundingMode.HALF_EVEN)—— 明确精度+舍入模式;金融常用HALF_EVEN(银行家舍入),减少统计偏差
建议在项目中封装通用工具方法,如MoneyUtils.div(amount, divisor, scale),统一约束舍入逻辑。
实体类设计:字段命名+注释+校验三位一体
光类型正确不够,还需语义清晰、防御到位:
- 字段名体现单位:如
orderAmountCents(分)、unitPriceYuan(元),避免amount这种模糊命名 - Javadoc必须注明单位、精度范围、是否含税、是否已折让等业务含义
- 配合JSR-303校验:如
@Min(value = 0L, message = "金额不能为负")、@Digits(integer = 12, fraction = 2, message = "金额最多12位整数+2位小数") - 构造器或Builder中做初始校验(如非空、非NaN、非Infinite)
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











