JJava 知识库
JAVA INTERVIEW

高频面试题

Java基础基础约 3 分钟

如果让你设计一套项目里的异常处理体系,业务异常、系统异常和第三方异常会怎么划分?

参考回答约 3 分钟 · 口语表达
先说结论

我会先控制影响,再按证据定位:Error 通常表示应用难以恢复的 JVM 级问题;Exception 表示程序可处理的异常,其中 RuntimeException 无需强制捕获。异常应在有能力处理的边界捕获,否则继续抛出并保留根因。 业务校验失败可以使用有明确语义的业务异常;

01

我会先确认影响范围,同时控制故障继续放大。区分 Error、受检异常和运行时异常,并掌握异常设计与资源释放。Error 通常表示应用难以恢复的 JVM 级问题;Exception 表示程序可处理的异常,其中 RuntimeException 无需强制捕获。异常应在有能力处理的边界捕获,否则继续抛出并保留根因。 业务校验失败可以使用有明确语义的业务异常;数据库、网络等底层异常通常应转换为当前层能理解的异常,但必须把原异常作为 cause。

02

止损之后,我会按请求链路建立证据,而不是凭经验猜。沿着「异常被抛出 → 调用栈逐层展开 → 匹配 catch 边界 → 执行资源释放 → 转换并返回稳定语义」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 异常被抛出 异常对象记录类型、消息、堆栈和 cause,抛出点应保留足以定位业务动作的上下文。 调用栈逐层展开 JVM 沿调用栈寻找可处理类型,期间跳过正常返回路径;把异常用于普通分支会引入控制流与对象创建成本。 java.lang.Throwable:cause、suppressed、stackTrace 保存方式。 java.lang.AutoCloseable:try-with-resources 逆序关闭契约。

03

定位时我最关注这些参数、指标和容量关系。构造“业务读取失败+close 失败”资源,断言主异常与 getSuppressed;再在接口边界验证错误码、HTTP 状态和日志只记录一次。

04

找到根因后先做最小修复,再用同样的流量验证。调用支付渠道超时时,渠道可能已扣款但响应丢失。简单 catch 后返回“失败”并自动重试会造成重复扣款。正确模型应把状态标记为“结果未知”,保存幂等请求号,通过查询与对账确认最终结果。 捕获 Exception 后只打印消息:错误码分布:把吞异常改为保留 cause 的语义转换。 重复记录同一异常造成日志风暴:非预期异常率:关闭中间层重复 ERROR 日志。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「非预期异常率」为主基线,记录值应满足「按接口保持稳定基线」;同时保存 错误码分布、非预期异常率,使后续变化能够回到同一时间轴比较。

05

恢复阶段要逐步放量,最后把监控和边界补齐。方案:更适合的场景:主要收益:代价与边界。 受检异常:调用方存在明确恢复动作:编译器强制处理契约:层级过多时声明噪声较大。 运行时异常:参数错误或无法就地恢复:API 简洁、便于统一边界处理:容易被忽略直到运行期。 结果对象/错误码:预期内业务分支且需批量处理:控制流明确、便于组合:可能丢失堆栈,类型设计更复杂。 选型至少带上 对象创建率、调用频次、输入规模和 API 边界,并用上面的量化基线验证;

排查与恢复时间线从目标到落地
01异常被抛出
02调用栈逐层展开
03匹配 catch 边界
04执行资源释放
05转换并返回稳定语义