大家好,我是程序员二叉。
简介
本文汇总 Java 后端面试高频核心考点,从金融业务BigDecimal选型原理与避坑、包装类缓存机制、面向对象多态、重载重写,到静态代码块执行顺序、反射全体系知识点,最后结合 Spring、MyBatis 源码落地场景逐一拆解。内容贴合日常开发与面试,干货密集,适合 Java 初学者夯实基础、面试突击复习 ,收藏即可快速查阅。欢迎点赞收藏关注。
一、为什么金融数据要用 BigDecimal?
1. 核心原因:float、double 会精度丢失
浮点数(float/double)是二进制存储 ,无法精确表示 0.1 这类十进制小数,会出现误差:
java
double a = 0.1 + 0.2; // 结果是 0.30000000000000004,不是 0.3
金融场景(金额、利率、税费)绝对不能容忍精度误差,否则会造成资金损失。
2. BigDecimal 优势
- 十进制精确表示,无精度丢失
- 支持高精度运算(加减乘除、舍入模式)
- 适合货币、订单、税务等金融计算
二、BigDecimal 常见坑点(面试必考)
1. 禁止用 new BigDecimal (double)
java
BigDecimal bad = new BigDecimal(0.1); // 精度丢失!
BigDecimal good = new BigDecimal("0.1"); // 正确
2. 除法必须指定舍入模式,否则抛异常
java
a.divide(b, 2, RoundingMode.HALF_UP); // 保留2位,四舍五入
3. 比较用 compareTo (),不要用 equals ()
equals() 会比较精度 ,compareTo() 只比较数值大小。
4. BigDecimal 是不可变类,运算后必须接收返回值:
java
BigDecimal num = new BigDecimal("10");
num.add(new BigDecimal("5")); // 错误,num 不变
num = num.add(new BigDecimal("5")); // 正确
5.不要用 toString () 做业务判断,可能出现科学计数法
三、自动装箱 & 拆箱
定义
- 装箱 :基本类型 → 包装类型(
int → Integer) - 拆箱 :包装类型 → 基本类型(
Integer → int)
示例
java
Integer a = 10; // 自动装箱:Integer.valueOf(10)
int b = a; // 自动拆箱:a.intValue()
四、Integer (-128 ~ 127) 缓存池
1. 是什么?
JVM 会提前创建好 -128 到 127 的 Integer 对象并缓存,用到时直接复用,不新建对象。
2. 目的
节省内存、提升性能(高频小数字最常用)。
五、Integer a=128; Integer b=128; a==b 为什么是 false?
==比较对象地址- -128~127 走缓存,同一个对象 →
true - 128 超出缓存范围 ,每次
valueOf()都会new 新对象
→ 两个不同对象,地址不同 → false
✅ 正确比较包装类值:用equals()
六、多态体现在哪些方面?
多态 = 同一个行为,不同对象表现不同形态
体现在 3 点:
- 方法重载(编译时多态)
- 方法重写(运行时多态)
- 父类引用指向子类对象
七、编译时多态 vs 运行时多态
| 类型 | 实现方式 | 绑定时机 |
|---|---|---|
| 编译时多态 | 方法重载 | 编译期确定 |
| 运行时多态 | 方法重写 | 运行期确定 |
- 编译时:编译器知道调用哪个方法
- 运行时:JVM 根据实际对象类型决定调用哪个方法
八、重载 vs 重写(面试必背)
重载(Overload)
- 同一个类中
- 方法名相同
- 参数列表不同(个数、类型、顺序)
- 与返回值无关
重写(Override)
- 子类继承父类
- 方法名、参数、返回值完全相同
- 子类权限不能更小
- 不能抛出更宽泛的异常
九、Java 为什么不支持多继承?
避免菱形问题(二义性)
如果两个父类有同名方法,子类不知道继承哪一个,会产生冲突。
Java 用接口多实现替代多继承。
十、静态代码块、实例代码块、构造方法执行顺序
执行顺序:
- 父类静态代码块
- 子类静态代码块
- 父类实例代码块
- 父类构造方法
- 子类实例代码块
- 子类构造方法
口诀:先静态,后实例;先父类,后子类
十一、什么是反射?
反射 = 程序运行时,获取类的完整信息并操作对象
可以拿到:类名、父类、接口、构造器、字段、方法、注解等。
核心类:
- Class
- Constructor
- Field
- Method
十二、反射为什么性能低?
- 解析类元数据(耗时)
- 方法调用是动态的,无法编译优化
- 权限检查(访问私有、安全校验)
- 无法 JIT 编译优化
十三、反射的优缺点
优点
- 运行时动态获取类信息
- 解耦,提高扩展性
- 框架底层核心技术
缺点 - 性能低
- 破坏封装(可访问私有)
- 代码可读性差
- 编译期无法检查错误
十四、获取 Class 对象的 3 种方式
- 对象.getClass ()
java
User user = new User();
Class<?> clazz = user.getClass();
- 类名.class
java
Class<?> clazz = User.class;
- Class.forName ("全类名")
java
Class<?> clazz = Class.forName("com.example.User");
区别
getClass():运行时确定.class:编译期确定,不触发类加载forName():动态加载类,会执行静态代码块(框架常用)
十五、反射创建对象
- 无参构造
java
Class<?> clazz = User.class;
User user = (User) clazz.newInstance(); // JDK9 废弃
User user = (User) clazz.getDeclaredConstructor().newInstance();
- 有参构造
java
Constructor constructor = clazz.getConstructor(String.class, int.class);
User user = (User) constructor.newInstance("张三", 20);
十六、反射调用私有字段 & 私有方法
1. 私有字段
java
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 关闭权限检查
field.set(user, "李四");
2. 私有方法
java
Method method = clazz.getDeclaredMethod("privateMethod");
method.setAccessible(true);
method.invoke(user);
十七、Spring / MyBatis 哪里用到了反射?(框架底层)
Spring 用到反射的地方
- IOC 容器创建 Bean
- 依赖注入 @Autowired
- AOP 动态代理
- 注解解析(@Controller、@Service)
- 配置文件实例化对象
MyBatis 用到反射的地方
- SQL 参数赋值
- 结果集映射 ResultSet → Java 对象
- Mapper 接口动态代理
- 注解 / XML 解析
总结
- 金融必须用
BigDecimal避免精度丢失 - 包装类缓存池、多态、重载重写是基础必考题
- 反射是框架灵魂,Spring/MyBatis 全靠它实现动态扩展
- 执行顺序、类加载、权限访问是面试常挖坑点