欢迎光临
我们一直在努力

Java异常处理:中小厂面试通关指南

Java异常处理:中小厂面试通关指南

2小时速通 | 面试必备 | 实战导向


一、异常体系全景图

1.1 继承关系

Throwable (所有异常的根类)
├── Error (系统级错误,不可恢复)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception (程序异常,可处理)
├── RuntimeException (运行时异常,非受检)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ClassCastException
│ ├── NumberFormatException
│ └── ArithmeticException
└── 其他Exception (编译时异常,受检)
├── IOException
├── SQLException
├── ClassNotFoundException
└── InterruptedException

1.2 两大分类对比

特性编译时异常 (Checked)运行时异常 (Unchecked)
继承关系 Exception (非RuntimeException) RuntimeException
编译要求 必须显式处理 (try-catch/throws) 无强制要求
发生时机 外部因素 (IO、网络、数据库) 代码逻辑问题
处理建议 必须处理 可提前规避
面试重点 了解常用类型 高频考点 ⭐⭐⭐

二、异常核心概念

2.1 四大关键字

try-catch:捕获并处理异常

try {
// 可能抛出异常的代码
} catch (SpecificException e) {
// 处理特定异常
} catch (Exception e) {
// 处理其他异常(放最后)
}

throws:声明方法可能抛出的异常

public void readFile(String path) throws IOException {
// 方法实现
}

throw:手动抛出异常对象

if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}

finally:无论如何都会执行的代码块

try {
// 业务代码
} finally {
// 资源清理(关闭文件、释放连接等)
// ⚠️ 严禁在此写 return
}

2.2 异常处理流程

程序执行

遇到异常

JVM创建异常对象

是否有try-catch?
├─ 是 → 匹配catch块 → 执行finally → 继续执行
└─ 否 → 向上抛出 → 最终到main → 程序终止


三、常见异常速查表

3.1 运行时异常 (重点掌握)

异常类触发场景代码示例解决方案
NullPointerException 调用null对象的方法/属性 String s = null;s.length(); 判空:if (s != null)
IllegalArgumentException 方法参数不合法 setAge(-1) 参数校验 + 抛异常
ArrayIndexOutOfBoundsException 数组下标越界 arr[10] (数组长度5) 校验:if (i < arr.length)
NumberFormatException 字符串转数字失败 Integer.parseInt("abc") try-catch 或正则预校验
ClassCastException 类型强转失败 (Dog) animal 先用 instanceof 判断
ArithmeticException 数学运算错误 10 / 0 校验除数不为0
IndexOutOfBoundsException 集合索引越界 list.get(100) 校验索引范围
ConcurrentModificationException 并发修改集合 遍历时删除元素 使用迭代器删除

3.2 编译时异常 (了解即可)

异常类触发场景处理方式
IOException 文件读写、网络IO失败 try-catch 或 throws
SQLException 数据库操作失败 统一捕获处理
ClassNotFoundException 反射找不到类 try-catch
InterruptedException 线程中断 恢复中断状态

四、异常处理最佳实践

4.1 业务场景对应规范

场景推荐做法示例
参数校验失败 抛自定义运行时异常 throw new ParamException("用户名不能为空")
业务逻辑违规 抛自定义业务异常 throw new BusinessException("余额不足")
文件/IO操作 捕获IOException try { … } catch (IOException e)
数据库操作 捕获SQLException 统一异常处理器
代码逻辑疏漏 提前规避 判空、范围校验

4.2 自定义异常模板

/**
* 业务异常基类
*/

public class BusinessException extends RuntimeException {
private int code;
private String message;

public BusinessException(String message) {
super(message);
this.code = 500;
this.message = message;
}

public BusinessException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}

public int getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}
}

4.3 异常处理原则

✅ 应该做的

  • 捕获具体异常,而非Exception
  • 记录异常日志(包含堆栈信息)
  • 给用户友好的错误提示
  • 在finally中释放资源
  • 自定义异常继承RuntimeException

❌ 不应该做的

  • 空catch块(吞掉异常)
  • finally中写return
  • 过度使用异常(正常逻辑用if判断)
  • 捕获Error(系统级错误)
  • 异常信息暴露敏感数据

五、面试手撕真题

真题1:字符串转整数(基础必考)

题目要求:

  • 实现 stringToInt(String s) 方法
  • 处理 null、空字符串、非数字字符串
  • 异常时返回0并打印错误信息
  • 禁止空catch块
  • 标准答案:

    public class StringConverter {
    public int stringToInt(String s) {
    // 1. 处理null
    if (s == null) {
    System.out.println("错误:字符串为null");
    return 0;
    }

    // 2. 处理空字符串
    if (s.trim().isEmpty()) {
    System.out.println("错误:字符串为空");
    return 0;
    }

    // 3. 尝试转换
    try {
    return Integer.parseInt(s);
    } catch (NumberFormatException e) {
    System.out.println("错误:'" + s + "' 无法转为数字");
    return 0;
    }
    }

    // 测试
    public static void main(String[] args) {
    StringConverter converter = new StringConverter();
    System.out.println(converter.stringToInt("123")); // 123
    System.out.println(converter.stringToInt("abc")); // 0
    System.out.println(converter.stringToInt(null)); // 0
    System.out.println(converter.stringToInt("")); // 0
    }
    }

    考点分析:

    • 边界条件处理(null、空字符串)
    • 具体异常捕获(NumberFormatException)
    • 异常信息输出
    • 返回默认值

    真题2:自定义参数校验异常(高频)

    题目要求:

  • 自定义 ParamValidException,继承RuntimeException
  • 包含 code(异常码)和 msg(异常信息)
  • 实现用户注册校验:用户名≥6位,密码≥8位
  • 标准答案:

    /**
    * 自定义参数校验异常
    */

    class ParamValidException extends RuntimeException {
    private int code;
    private String msg;

    public ParamValidException(String msg) {
    super(msg);
    this.code = 400;
    this.msg = msg;
    }

    public ParamValidException(int code, String msg) {
    super(msg);
    this.code = code;
    this.msg = msg;
    }

    public int getCode() {
    return code;
    }

    public String getMsg() {
    return msg;
    }
    }

    /**
    * 用户注册服务
    */

    public class UserService {
    public void register(String username, String password) {
    // 用户名校验
    if (username == null || username.length() < 6) {
    throw new ParamValidException(400, "用户名长度不能小于6位");
    }

    // 密码校验
    if (password == null || password.length() < 8) {
    throw new ParamValidException(400, "密码长度不能小于8位");
    }

    System.out.println("注册成功:" + username);
    }

    // 测试
    public static void main(String[] args) {
    UserService service = new UserService();

    try {
    service.register("user", "12345678"); // 用户名太短
    } catch (ParamValidException e) {
    System.out.println("异常码:" + e.getCode());
    System.out.println("异常信息:" + e.getMsg());
    }

    try {
    service.register("username", "123"); // 密码太短
    } catch (ParamValidException e) {
    System.out.println("异常码:" + e.getCode());
    System.out.println("异常信息:" + e.getMsg());
    }

    service.register("username", "12345678"); // 成功
    }
    }

    考点分析:

    • 自定义异常类设计
    • 继承RuntimeException(无需throws)
    • 构造方法重载
    • 业务参数校验
    • 异常信息封装

    真题3:try-catch-finally执行顺序(坑点题)

    题目:分析以下代码的执行顺序和返回值

    public class FinallyTest {
    public static int test() {
    int num = 10;
    try {
    System.out.println("1. try块执行");
    num = num / 0; // 抛出异常
    return num;
    } catch (ArithmeticException e) {
    System.out.println("2. catch块执行");
    num = 20;
    return num;
    } finally {
    System.out.println("3. finally块执行");
    num = 30;
    return num; // ⚠️ 问题代码
    }
    }

    public static void main(String[] args) {
    System.out.println("最终返回值:" + test());
    }
    }

    输出结果:

    1. try块执行
    2. catch块执行
    3. finally块执行
    最终返回值:30

    问题分析:

  • try块抛出异常,跳转到catch
  • catch块准备返回20,但finally必须先执行
  • finally块执行并返回30,覆盖了catch的返回值
  • 严重问题:finally中的return会覆盖正常返回值
  • 正确写法:

    public static int test() {
    int num = 10;
    try {
    System.out.println("1. try块执行");
    num = num / 0;
    return num;
    } catch (ArithmeticException e) {
    System.out.println("2. catch块执行");
    num = 20;
    return num;
    } finally {
    System.out.println("3. finally块执行");
    num = 30; // 只修改变量,不return
    // 资源清理代码
    }
    }
    // 此时返回值为20(正确)

    考点分析:

    • try-catch-finally执行顺序
    • finally中return的坑
    • 异常处理流程
    • 代码规范

    六、面试高频问答

    Q1:Exception和Error的区别?

    答:

    • Exception:程序异常,可预见可处理,如空指针、IO异常
    • Error:系统级错误,不可恢复,如内存溢出、栈溢出
    • 关键区别:Exception应该捕获处理,Error无需处理(程序无法恢复)

    Q2:运行时异常和编译时异常的区别?

    答:

    • 编译时异常:继承Exception但非RuntimeException,编译期必须处理
    • 运行时异常:继承RuntimeException,编译期无强制要求
    • 使用场景:编译时异常用于外部因素(IO、网络),运行时异常用于代码逻辑问题

    Q3:throw和throws的区别?

    答:

    • throw:方法内部手动抛出异常对象,throw new Exception()
    • throws:方法签名声明可能抛出的异常,void method() throws Exception
    • 记忆技巧:throw抛对象,throws声明类型

    Q4:finally一定会执行吗?

    答:
    不一定,以下情况不执行:

  • JVM退出(System.exit())
  • 守护线程中,主线程结束
  • 死循环或死锁
  • 断电、进程被杀
  • 正常情况下,即使try/catch中有return,finally也会执行

    Q5:如何自定义异常?

    答:

    // 1. 继承RuntimeException(推荐)或Exception
    public class CustomException extends RuntimeException {
    private int code;

    // 2. 提供构造方法
    public CustomException(String message) {
    super(message);
    }

    public CustomException(int code, String message) {
    super(message);
    this.code = code;
    }

    // 3. 提供getter方法
    public int getCode() {
    return code;
    }
    }

    Q6:try-catch会影响性能吗?

    答:

    • 正常流程:性能影响极小,可忽略
    • 异常抛出:创建异常对象、填充堆栈,有一定开销
    • 建议:不要用异常控制正常业务流程,用if判断

    Q7:多个catch块的顺序有要求吗?

    答:
    有要求,必须从子类到父类排列:

    try {
    // 代码
    } catch (FileNotFoundException e) { // 子类
    // 处理
    } catch (IOException e) { // 父类
    // 处理
    } catch (Exception e) { // 最父类
    // 处理
    }

    原因:Java按顺序匹配catch块,父类会捕获所有子类异常


    七、避坑指南

    坑点1:空catch块

    // ❌ 错误:吞掉异常
    try {
    // 代码
    } catch (Exception e) {
    // 什么都不做
    }

    // ✅ 正确:至少打印日志
    try {
    // 代码
    } catch (Exception e) {
    e.printStackTrace();
    // 或使用日志框架
    log.error("操作失败", e);
    }

    坑点2:finally中return

    // ❌ 错误:覆盖正常返回值
    try {
    return 1;
    } finally {
    return 2; // 最终返回2,隐藏了try的返回
    }

    // ✅ 正确:finally只做清理
    try {
    return 1;
    } finally {
    // 关闭资源
    connection.close();
    }

    坑点3:捕获Exception

    // ❌ 错误:无法区分异常类型
    try {
    // 代码
    } catch (Exception e) {
    // 所有异常都这样处理
    }

    // ✅ 正确:捕获具体异常
    try {
    // 代码
    } catch (IOException e) {
    // IO异常处理
    } catch (SQLException e) {
    // 数据库异常处理
    }

    坑点4:资源未关闭

    // ❌ 错误:异常时资源泄漏
    FileInputStream fis = new FileInputStream("file.txt");
    try {
    // 读取文件
    } catch (IOException e) {
    e.printStackTrace();
    }
    // 如果try中抛异常,fis未关闭

    // ✅ 正确:使用try-with-resources
    try (FileInputStream fis = new FileInputStream("file.txt")) {
    // 读取文件
    } catch (IOException e) {
    e.printStackTrace();
    }
    // 自动关闭资源

    坑点5:异常信息暴露敏感数据

    // ❌ 错误:暴露数据库信息
    throw new Exception("数据库连接失败:jdbc:mysql://192.168.1.100:3306/db");

    // ✅ 正确:友好提示
    throw new Exception("系统繁忙,请稍后重试");


    八、核心知识点速记卡

    🎯 必背知识点

  • 异常分类

    • 编译时异常:必须处理,外部因素
    • 运行时异常:可选处理,代码逻辑
  • 四大关键字

    • try-catch:捕获处理
    • throws:声明抛出
    • throw:手动抛出
    • finally:必定执行(资源清理)
  • 常见异常

    • NullPointerException:空指针
    • IllegalArgumentException:参数非法
    • NumberFormatException:数字格式
    • IOException:IO操作
    • SQLException:数据库操作
  • 自定义异常

    • 继承RuntimeException
    • 包含code和message
    • 提供构造方法和getter
  • 处理原则

    • 捕获具体异常
    • 记录日志
    • 友好提示
    • finally清理资源
    • 禁止空catch
    • 禁止finally中return
  • 📝 面试检查清单

    • 能说清异常体系结构
    • 能区分编译时和运行时异常
    • 能手写字符串转整数(含异常处理)
    • 能手写自定义异常类
    • 能说清try-catch-finally执行顺序
    • 能说清finally中return的坑
    • 能列举5个以上常见异常
    • 能说清throw和throws的区别
    • 能说清Exception和Error的区别
    • 知道try-with-resources用法

    九、实战代码模板

    模板1:统一异常处理器(Spring Boot)

    这个在博主之前的博文里面有,感兴趣的可以翻看博主以前的文章哦!😊

    @RestControllerAdvice
    public class GlobalExceptionHandler {

    @ExceptionHandler(ParamValidException.class)
    public Result handleParamValid(ParamValidException e) {
    return Result.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
    log.error("系统异常", e);
    return Result.error("系统繁忙,请稍后重试");
    }
    }

    模板2:业务方法异常处理

    public void businessMethod(String param) {
    // 1. 参数校验
    if (param == null || param.isEmpty()) {
    throw new ParamValidException("参数不能为空");
    }

    // 2. 业务逻辑
    try {
    // 数据库操作
    dao.save(param);
    } catch (SQLException e) {
    log.error("数据库操作失败", e);
    throw new BusinessException("操作失败,请重试");
    }
    }

    模板3:资源管理

    // 推荐:try-with-resources
    public String readFile(String path) {
    try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
    return reader.readLine();
    } catch (IOException e) {
    log.error("文件读取失败", e);
    return null;
    }
    }


    十、学习建议

    2小时学习路径

    第1小时:理论基础

    • 0-20分钟:异常体系和分类
    • 20-40分钟:四大关键字用法
    • 40-60分钟:常见异常速查

    第2小时:实战练习

    • 0-30分钟:手撕3道真题
    • 30-50分钟:背诵面试问答
    • 50-60分钟:复习避坑指南

    进阶学习

  • 源码阅读:Throwable类的实现
  • 框架应用:Spring的异常处理机制
  • 性能优化:异常对性能的影响
  • 日志规范:异常日志的最佳实践

  • 结语

    Java异常处理是面试必考基础,中小厂重点考察:

  • 基础概念理解
  • 常见异常识别
  • 手写代码能力
  • 规范和避坑
  • 掌握本文内容,足以应对90%的异常相关面试题。建议:

    • 理论部分理解记忆
    • 真题代码手写3遍
    • 面试前快速复习速记卡

    祝面试顺利!💪


    作者:[识君啊]

    赞(0)
    未经允许不得转载:171主机测评 » Java异常处理:中小厂面试通关指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址