从金融场景出发:深入理解 BigDecimal 与 Java 核心特性

发布时间:2026/5/26 22:58:50

从金融场景出发:深入理解 BigDecimal 与 Java 核心特性 数据类型为什么金融类型的数据要使用BigDecimal首先double会出现精度丢失的问题因为double底层采用的是二进制浮点计算的方式二进制有些情况下不能准确的表示某一个小数就好像10进制不能准确的表示1/3一样的道理。二进制如果要表示一个小数的话这个小数只能够表示用 1/(2^N) 的任意组合因此0.1是不能够被精确表示对因为它不能被表示成为1/(2^N)的和的形式接着可以考虑举一个例子就是0.6元没有办法去购买一个0.5元的物品0.1元的物品所以在很多的金融体系当中我们都是用 BigDecimal 来精确的表示金额的余额确保精确的十进制表示首先需要提到精度丢失的问题--深入double底层表示二进制浮点数的计算方式--明确小数只能用 1/(^N)的组合来进行表示--就导致了很多小数不能够被精确表示--接着举一个小例子所以 BigDecimal 用的是精确十进制来进行表示对应的数值既然你说要用这个数据类型bigDecimal的常见坑点有哪些equals会同时比较数值和标度scale即小数位数。new BigDecimal(1.0)和new BigDecimal(1.00)数值相同但 scale 不同所以equals返回false。✅ 正确做法比较数值大小用compareTo除法不设精度直接抛异常BigDecimal做除法时如果除不尽如1/3且没有指定舍入模式会立即抛出ArithmeticException: Non-terminating decimal expansion。✅ 正确做法明确指定精度和舍入模式a.divide(b, 2, RoundingMode.HALF_UP) // 或使用 MathContext a.divide(b, new MathContext(10, RoundingMode.HALF_UP))性能坑别在热循环里滥用BigDecimal比int/long/double慢几十到上百倍而且对象创建开销大。 如果你的场景只是金钱的简单累加可以用long分为单位代替到显示层再转成BigDecimal格式化或者用专门的货币类库。BigDecimal常见坑点主要有几个第一不要用new BigDecimal(0.1)因为0.1这个double本身就有精度误差应该用new BigDecimal(0.1)或BigDecimal.valueOf(0.1)。第二比较大小不要随便用equals()因为equals()会比较精度位数比如1.0和1.00不相等一般业务比较用compareTo()。第三除法可能出现无限循环小数比如1 / 3如果不指定精度和舍入模式会抛异常所以要写a.divide(b, 2, RoundingMode.HALF_UP);另外BigDecimal是不可变对象add()、subtract()、multiply()后要接收返回值否则结果不会变。什么是自动装箱和拆箱自动装箱和拆箱是 Java 中基本数据类型和包装类型之间的自动转换。比如int自动转换成Integer这叫自动装箱Integer自动转换成int这叫自动拆箱。它简化了代码但如果包装类型为null时发生拆箱会导致NullPointerException。那么自动装箱和拆箱有什么用为什么需要有自动装箱和拆箱的机制呢【补充】自动装箱和拆箱主要是为了让基本类型能更方便地和包装类型配合使用尤其是在集合、泛型、反射等只能使用对象的场景中。比如ListInteger不能直接存int有了自动装箱后写list.add(1)就可以自动把int转成Integer。 它的作用是减少手动转换代码让基本类型和对象类型之间使用起来更自然。Integer的-128-127缓存池Integer的-128 到 127缓存池是指 Java 会提前缓存这一范围内的Integer对象。当使用自动装箱或Integer.valueOf()时如果值在-128~127之间会复用缓存对象所以Integer a 127; Integer b 127; System.out.println(a b); // true但超过这个范围通常会创建新对象Integer c 128; Integer d 128; System.out.println(c d); // false所以面试里要注意包装类型比较值应该用equals()不要用。面向对象你能不能说一下面向对象的三大特性分别是什么吗封装封装是指将对象的属性数据和行为方法封装到了一起对外隐藏了内部的细节只暴露出几个接口来实现与外界的交互一方面保证了信息的安全性也简化了编程的开发过程继承子类可以继承父类的属性和方法对父类的属性和方法实现了共享多态多态允许不同类的对象对同一消息做出响应但表现出不同的行为即方法的多样性。多态其实是一种能力——同一个行为具有不同的表现形式换句话说就是执行一段代码Java 在运行时能根据对象类型的不同产生不同的结果。多态体现在哪些方面这个问题应该怎么去进行回答呢多态主要体现在两个方面方法重写和父类引用指向子类对象。比如同样调用animal.eat()如果实际对象是Dog就执行狗的eat()如果是Cat就执行猫的eat()。 本质上就是编译看父类运行看子类具体执行哪个方法由实际对象决定。什么是编译时多态和运行时多态编译时多态主要指方法重载在编译阶段就能确定调用哪个方法比如方法名相同但参数类型或参数个数不同。运行时多态主要指方法重写也就是父类引用指向子类对象程序运行时根据实际对象类型决定调用哪个方法。简单说重载是编译时多态重写是运行时多态。重载和重写的区别如果一个类有多个名字相同但参数个数不同的方法我们通常称这些方法为方法重载。如果子类具有和父类一样的方法参数相同、返回类型相同、方法名相同但方法体不同我们称之为方法重写。Java为什么不支持多继承Java 不支持类的多继承主要是为了避免菱形继承问题。比如两个父类中有同名方法子类同时继承它们时编译器很难判断该调用哪个父类的方法容易造成歧义。 所以 Java 只支持单继承但可以通过实现多个接口来达到多继承能力的扩展。静态代码块实例代码块构造方法的执行顺序是什么执行顺序是静态代码块 → 实例代码块 → 构造方法。如果有继承关系顺序是父类静态代码块 → 子类静态代码块 → 父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法。静态代码块只在类加载时执行一次实例代码块和构造方法每次创建对象都会执行。您不能解释一下为什么可以。静态代码块属于类本身所以在类加载的时候先执行而且只执行一次实例代码块属于对象只有创建对象时才执行并且它会在构造方法之前执行用来做对象的通用初始化构造方法最后执行因为它是对象初始化的最后一步。有继承时必须先初始化父类再初始化子类所以顺序才是父类静态 → 子类静态 → 父类实例 → 父类构造 → 子类实例 → 子类构造。

相关新闻