第2章 Java基础高频面试题:面向对象、异常机制、泛型与反射
本章聚焦 Java 基础三大高频模块——面向对象、异常机制、泛型与反射。面试官常从概念切入,逐步深入原理与实战。掌握通用回答框架并预判追问路径,是拿下基础分的关键。
| 主题 | 核心考点 | 常见陷阱 | 加分点 |
|---|---|---|---|
| 面向对象 | 封装、继承、多态;动态绑定原理 | 混淆重载与重写;忽略继承的耦合问题 | 结合设计原则(开闭、里氏替换) |
| 异常机制 | 异常体系;finally 执行规则;异常链 | finally 中 return/抛异常;吞异常 | try-with-resources;自定义异常规范 |
| 泛型与反射 | 类型擦除;桥方法;反射 API | 依赖运行时泛型信息;反射性能滥用 | 缓存反射对象;区分通配符与类型参数 |
面试问题:请谈谈你对面向对象三大特性(封装、继承、多态)的理解。
- 面试官考察点
- 基本概念是否清晰,能否结合实际说明各自作用,是否具备面向对象设计思维。
- 回答框架
-
- ① 分别定义封装、继承、多态。
- ② 各举一个典型使用场景(如封装用 JavaBean、继承用 extends、多态用接口回调)。
- ③ 总结三者协同关系:封装提供模块化,继承实现代码复用,多态支撑扩展性。
- 示例回答
-
封装通过 private 隐藏内部状态,提供 getter/setter 控制访问,提升安全性;继承用 extends 复用父类代码,减少重复,但需注意里氏替换原则;多态通过父类引用指向子类对象,运行时动态绑定,实现“对扩展开放,对修改关闭”。三者共同构成面向对象的核心思想。 - 可能追问
-
- 多态的实现原理? 答:JVM 通过虚方法表实现动态绑定,调用时根据实际对象类型查找方法地址。
- 继承有什么缺点? 答:高耦合,修改父类可能影响所有子类;优先考虑组合。
- 封装与开闭原则的关系? 答:封装隐藏实现细节,对外暴露稳定接口,符合开闭原则。
- 避坑提醒
- 不要只背诵定义,必须用具体代码场景支撑;区别编译时多态(重载)和运行时多态(重写),面试时优先讲运行时多态。
面试问题:Java 异常的分类及处理机制是什么?finally 块在什么情况下不会执行?什么是异常链?
- 面试官考察点
- 对异常体系结构的掌握,finally 执行细节的理解,以及异常处理的最佳实践。
- 回答框架
-
- ① 异常体系:Throwable 下分 Error(不可处理)和 Exception(可处理);Exception 分受检异常和运行时异常。
- ② try-catch-finally 流程:try 中异常 → 匹配 catch → 无论是否异常 finally 均执行(除了关机、JVM 崩溃、线程死亡等极端情况)。
- ③ 异常链:通过构造函数传递 cause 参数,保留原始异常信息。
- 示例回答
-
Java 异常根类是 Throwable,Error 如 OutOfMemoryError 无需捕获,Exception 分为受检(如 IOException,必须处理)和非受检(如 NullPointerException,可选)。finally 正常情况下始终执行,但遇到 System.exit()、JVM 宕机或线程被 kill 时不会执行。异常链通过 new Exception("msg", cause) 保留根因。 - 可能追问
-
- try-with-resources 原理? 答:自动调用 AutoCloseable 的 close,编译后生成 try-finally 结构。
- 异常性能开销? 答:创建异常对象时填充栈轨迹较慢;控制流中避免使用异常。
- 自定义异常如何设计? 答:继承 Exception(受检)或 RuntimeException(非受检),提供序列化支持。
- 避坑提醒
- 不要在 finally 中写 return 或抛出异常,否则会覆盖 catch 中的返回值/异常;不要捕获 Exception 后只打印日志不抛出不处理(吞异常)。
面试问题:Java 泛型的实现原理(类型擦除)是什么?什么是桥方法?反射的常用 API 及性能优化?
- 面试官考察点
- 泛型底层机制、桥方法产生原因、反射实际使用经验及性能意识。
- 回答框架
-
- ① 泛型通过类型擦除实现:编译时检查类型,运行时擦除为原始类型(如 List → List),插入自动强转。
- ② 桥方法:子类覆盖父类泛型方法时,编译器生成一个签名相同但参数为原始类型的方法,维持多态。
- ③ 反射常用类:Class、Method、Field、Constructor;通过 getMethod、invoke 调用。
- ④ 性能优化:缓存 Method 对象,避免重复查找;优先用接口调用而非反射。
- 示例回答
-
泛型擦除后 List<String> 变为 List,元素类型转为 Object,add 时自动 cast。桥方法解决子类重写时的签名冲突。反射通过 Class.forName 加载类,用 method.invoke 执行,但每次调用都会安全检查;建议将 Method 实例缓存到 Map 中,减少开销。 - 可能追问
-
- 通配符 ? 和类型参数 T 的区别? 答:? 用于不确定类型,T 用于声明类型参数绑定。
- 如何通过反射获取泛型实际类型? 答:不能直接获取,但可以通过 getGenericSuperclass 等方法间接解析。
- 反射破坏封装性如何防范? 答:安全管理器限制 setAccessible。
- 避坑提醒
- 不要滥用泛型原始类型(如 List 而非 List<T>);反射调用时注意方法参数类型需完全匹配(包括装箱类型);优先使用泛型而非反射动态调用。v