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:捕获并处理异常
java 复制代码
try {
    // 可能抛出异常的代码
} catch (SpecificException e) {
    // 处理特定异常
} catch (Exception e) {
    // 处理其他异常(放最后)
}
throws:声明方法可能抛出的异常
java 复制代码
public void readFile(String path) throws IOException {
    // 方法实现
}
throw:手动抛出异常对象
java 复制代码
if (age < 0) {
    throw new IllegalArgumentException("年龄不能为负数");
}
finally:无论如何都会执行的代码块
java 复制代码
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 自定义异常模板

java 复制代码
/**
 * 业务异常基类
 */
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:字符串转整数(基础必考)

题目要求:

  1. 实现 stringToInt(String s) 方法
  2. 处理 null、空字符串、非数字字符串
  3. 异常时返回0并打印错误信息
  4. 禁止空catch块

标准答案:

java 复制代码
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:自定义参数校验异常(高频)

题目要求:

  1. 自定义 ParamValidException,继承RuntimeException
  2. 包含 code(异常码)和 msg(异常信息)
  3. 实现用户注册校验:用户名≥6位,密码≥8位

标准答案:

java 复制代码
/**
 * 自定义参数校验异常
 */
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执行顺序(坑点题)

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

java 复制代码
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

问题分析:

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

正确写法:

java 复制代码
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一定会执行吗?

答:

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

  1. JVM退出(System.exit())
  2. 守护线程中,主线程结束
  3. 死循环或死锁
  4. 断电、进程被杀

正常情况下,即使try/catch中有return,finally也会执行

Q5:如何自定义异常?

答:

java 复制代码
// 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块的顺序有要求吗?

答:

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

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

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


七、避坑指南

坑点1:空catch块

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

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

坑点2:finally中return

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

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

坑点3:捕获Exception

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

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

坑点4:资源未关闭

java 复制代码
// ❌ 错误:异常时资源泄漏
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:异常信息暴露敏感数据

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

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

八、核心知识点速记卡

🎯 必背知识点

  1. 异常分类

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

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

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

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

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

📝 面试检查清单

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

九、实战代码模板

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

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

java 复制代码
@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:业务方法异常处理

java 复制代码
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:资源管理

java 复制代码
// 推荐: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分钟:复习避坑指南

进阶学习

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

结语

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

  1. 基础概念理解
  2. 常见异常识别
  3. 手写代码能力
  4. 规范和避坑

掌握本文内容,足以应对90%的异常相关面试题。建议:

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

祝面试顺利!💪


作者:[识君啊]

相关推荐
qyzm3 小时前
天梯赛练习(3月13日)
开发语言·数据结构·python·算法·贪心算法
月月玩代码3 小时前
Actuator,Spring Boot应用监控与管理端点!
java·spring boot·后端
leluckys3 小时前
swift- Swift中常见的面试题
开发语言·汇编·swift
BUG_MeDe3 小时前
json格式字符串解析的简单使用 libjson-c
c语言·开发语言·json
CoderCodingNo4 小时前
【GESP】C++五级练习题 luogu-P1182 数列分段 Section II
开发语言·c++·算法
阿珍爱上了阿强,在一个有星星的夜晚4 小时前
node后端页面性能监测分析
java·学习方法
Java程序之猿4 小时前
SpringBoot + camel+IBM MQ实现消息队列处理
java·spring boot·mybatis