第6篇:Java异常有哪几种

📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第5篇:装箱和拆箱 |
➡️ 下一篇:第7篇:Java面向对象简介

一、核心知识点

  • 异常体系分类:检查型异常(Checked Exception)、运行时异常(Runtime Exception)、错误(Error)
  • 异常处理关键字:trycatchfinallythrowsthrow
  • 常见异常类:NullPointerExceptionArrayIndexOutOfBoundsExceptionIOExceptionSQLException
  • 异常处理规范和最佳实践

二、通俗讲解(1分钟开心学)

1. 异常是什么?

程序运行时会遇到各种意外情况,比如:

  • 用户输入的年龄是-5岁(不合理)
  • 要读取的文件不存在
  • 网络突然断开
  • 数组下标越界

这些意外就是“异常”。Java 把它们分成了三大类:

2. 三大异常类型

类型 英文名 特点 例子
检查型异常 Checked Exception 编译器强制处理(要么 try-catch,要么 throws),代表可以预见的、外部环境引起的问题 IOException(文件找不到)、SQLException(数据库连接失败)
运行时异常 Runtime Exception 不强制处理,通常由程序逻辑错误引起。出现这种异常说明代码写错了,应该修改代码而不是捕获 NullPointerException(空指针)、ArrayIndexOutOfBoundsException(数组越界)、ArithmeticException(除以零)
错误 Error 不是程序能处理的,JVM 内部严重问题 OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)

3. 异常处理的关键字

  • try:放可能出错的代码
  • catch:捕获特定异常并处理
  • finally:无论是否发生异常,都会执行的代码(用于释放资源)
  • throw:手动抛出异常对象
  • throws:声明方法可能抛出的异常,交给调用者处理

生活类比
检查型异常就像你出门前天气预报说有雨。你必须带伞(处理异常),否则会被淋湿。
运行时异常就像你在晴天突然踩到香蕉皮滑倒。这通常是你自己不小心(代码写错),而不是环境问题。
错误就像突然地震了,你个人无能为力,只能靠整个系统(JVM)处理。

三、实操代码案例 + 场景说明

场景:从文件读取用户配置,如果文件不存在则尝试使用默认配置。

1. 检查型异常处理(必须处理)

import java.io.*;

public class CheckedExceptionDemo {
    public static void main(String[] args) {
        // 方式一:try-catch 捕获处理
        try {
            FileReader fr = new FileReader("config.txt");
            BufferedReader br = new BufferedReader(fr);
            String line = br.readLine();
            System.out.println("配置内容:" + line);
            br.close();
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在,使用默认配置");
        } catch (IOException e) {
            System.out.println("读取文件出错:" + e.getMessage());
        }
        
        // 方式二:throws 向上抛出(交给调用者)
        // 适用于方法本身无法处理异常的情况
    }
    
    // 声明 throws,表示调用者必须处理
    public void readConfig() throws IOException {
        FileReader fr = new FileReader("config.txt");
        // ...
    }
}

2. 运行时异常(不强制处理,但最好修复代码)

public class RuntimeExceptionDemo {
    public static void main(String[] args) {
        // 错误写法:运行时异常不处理会崩溃
        int[] arr = {1, 2, 3};
        // System.out.println(arr[3]);  // 运行会抛 ArrayIndexOutOfBoundsException
        
        // 正确做法:修复代码,而不是捕获
        if (args.length > 0) {
            System.out.println(args[0]);
        }
        
        // 如果确实需要捕获(比如防御性编程),也可以,但不推荐
        try {
            int a = 10 / 0;
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0,已处理");
        }
    }
}

3. finally 与 try-with-resources

public class FinallyDemo {
    public static void main(String[] args) {
        // 传统方式:手动关闭资源
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("data.txt");
            int data = fis.read();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();  // 关闭也可能抛异常
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
        // 优雅方式:try-with-resources(JDK7+)
        try (FileInputStream fis2 = new FileInputStream("data.txt")) {
            int data = fis2.read();
        } catch (IOException e) {
            e.printStackTrace();
        } // 自动关闭,不需要 finally
    }
}

4. 自定义异常

// 自定义检查型异常
class AgeInvalidException extends Exception {
    public AgeInvalidException(String message) {
        super(message);
    }
}

public class CustomExceptionDemo {
    public static void checkAge(int age) throws AgeInvalidException {
        if (age < 0 || age > 150) {
            throw new AgeInvalidException("年龄非法:" + age);
        }
        System.out.println("年龄合法");
    }
    
    public static void main(String[] args) {
        try {
            checkAge(-5);
        } catch (AgeInvalidException e) {
            System.out.println("捕获异常:" + e.getMessage());
        }
    }
}

四、避坑要点

错误/误区 后果 正确做法
捕获异常后什么都不做(空 catch) 问题被掩盖,难以排查 至少记录日志或打印堆栈
catch 的顺序把父类放在子类前面 子类异常永远捕获不到 子类在前,父类在后
finally 块中 return 会覆盖 trycatch 中的 return 避免在 finallyreturn
用异常控制正常业务流程 性能极差 if-else 判断
throws 声明了检查型异常却从不抛出 造成调用者无意义的处理 只声明真正会抛出的异常

五、面试高频考点

Q1:检查型异常和运行时异常的区别?

检查型异常编译器强制处理(try-catchthrows),代表可恢复的外部错误(如文件不存在)。运行时异常不强制处理,代表编程错误(如空指针、越界)。Error 是 JVM 内部错误。

Q2:throwthrows 的区别?

throw 用于实际抛出异常对象(在方法体内);throws 写在方法签名后,声明该方法可能抛出的异常类型。

Q3:finally 块一定会执行吗?

不一定。如果 trycatch 中调用了 System.exit(),或者 JVM 崩溃,finally 不会执行。

Q4:try-with-resources 的原理?

实现了 AutoCloseable 接口的资源可以在 try 括号中声明,try 结束后自动调用 close()。编译器会生成 finally 块并处理异常抑制(addSuppressed)。

六、练习题

  1. 简答:写出至少5种运行时异常的名称。
  2. 编程:编写一个方法 int divide(int a, int b),当 b==0 时抛出自定义运行时异常 DivideByZeroException
  3. 代码改错:指出下面代码的问题并修正。
    try {
        int[] arr = new int[5];
        System.out.println(arr[5]);
    } catch (Exception e) {
        e.printStackTrace();
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println("越界");
    }
    


📊 你的学习进度

  • 当前:第6篇 / 共44篇 · 第二阶段:核心语法与面向对象(第5~20篇)
  • ✅ 已完成:第1~5篇
  • 📖 正在学:第6篇
  • ⏳ 待学习:第7~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


👉 下一篇预告

《Java面向对象简介》

内容简介:面向对象三大特征(封装、继承、多态)是什么?类和对象有什么区别?

💡 学完这篇,你将理解为什么 Java 是面向对象的语言,并能用 OOP 思想设计简单程序。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!

更多推荐