Java:异常机制(概念、分类、处理、常见异常)

一、什么是异常

在程序运行过程中,阻止程序正常执行的错误情况,就叫做异常。

如果程序出现异常且不处理,会发生两件事:

  1. 程序直接崩溃终止
  2. 后面的代码不再执行
public class Demo01 {
    public static void main(String[] args) {
        System.out.println("第一行");
        int a = 10 / 0;          // 这里抛异常
        System.out.println("第二行"); // 永远不会执行
    }
}

运行结果:

第一行
Exception in thread "main" java.lang.ArithmeticException: / by zero

可以看到,"第二行" 没有打印出来——异常一旦抛出,它之后的代码就停了。

异常不等于语法错误

  • 语法错误:编译就失败,程序根本跑不起来。比如少写分号、括号不配对。
  • 异常:代码语法完全没问题,编译能通过,但运行时出了错。
int a = 10 / 0;        // 语法没错,运行才炸 → 异常
int b                  // 少了分号 → 语法错误,编译不过

二、异常的体系结构

Java 中所有异常都继承自 Throwable。Throwable 下面只有两个直接子类:

Throwable
├── Error          (错误:管不了,交给 JVM)
└── Exception      (异常:能处理,我们自己管)
    ├── 编译时异常(受检)   必须处理
    └── 运行时异常(非受检) 运行才暴露

1. Error(错误)

非常严重的问题,程序一般处理不了,通常只能让程序终止。

// StackOverflowError:方法一直调自己,栈撑爆了
public static void die() { die(); }

常见的 Error:OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)。遇到 Error,不要去 catch。

2. Exception(异常)

程序本身可以处理的异常,这是我们要重点掌握的,它又分为两类。

① 编译时异常(受检异常)

编译阶段就会报错,不处理就编不过。

// 编译报错:未处理的异常 IOException
FileInputStream fis = new FileInputStream("a.txt");

典型代表:IOException、SQLException、FileNotFoundException。

② 运行时异常(非受检异常)

编译阶段不报错,运行到才会暴露。

String s = null;
System.out.println(s.length());  // 运行才炸:NullPointerException

典型代表:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。

一句话记住:外部环境惹的祸(文件、网络、数据库)→ 编译时异常;自己代码写错(空指针、越界、除零)→ 运行时异常。

三、异常的处理方式

1. 捕获:try-catch

自己把异常处理掉,程序就不会崩。

public class Demo02 {
    public static void main(String[] args) {
        try {
            int r = 10 / 0;
            System.out.println("结果是" + r);
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        }
        System.out.println("程序继续运行");
    }
}

运行结果:

除数不能为0
程序继续运行

多个 catch 时,子类要写在父类前面,否则编译器报错:

try {
    int n = Integer.parseInt(s);
    System.out.println(10 / n);
} catch (NumberFormatException e) {
    System.out.println("不是数字");
} catch (ArithmeticException e) {
    System.out.println("除数为0");
}

2. 抛出:throws

自己不处理,声明“我会抛”,交给调用者去管。

static void readFile() throws IOException {
    FileInputStream fis = new FileInputStream("a.txt"); // 编译时异常,必须处理或声明
}

3. 手动抛:throw

主动制造并抛出一个异常,常用于参数不合法时。

static void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("年龄不能为负");
    }
    System.out.println("年龄:" + age);
}

4. finally:无论怎样都会执行

try {
    System.out.println(10 / 0);
} catch (ArithmeticException e) {
    System.out.println("出错了");
} finally {
    System.out.println("我一定会被执行");
}

运行结果:

出错了
我一定会被执行

finally 常用于关闭资源。注意:finally 里不要写 return,否则会把 try 里的值吞掉:

static int bad() {
    try { return 1; }
    finally { return 2; }   // 调用者拿到 2,1 被吞了
}

5. try-with-resources(推荐)

只要类实现了 AutoCloseable,放进 try() 的小括号,无论是否异常都会自动调用 close()。

try (FileInputStream fis = new FileInputStream("a.txt")) {
    fis.read();
} catch (IOException e) {
    e.printStackTrace();
}

四、throw 与 throws

这是最容易混的一对,区别只看两点:写在哪、写什么。

对比 throw throws
本质 动作:真的抛一个异常 声明:预告自己可能抛
位置 方法体内部 方法签名的括号之后
后面跟 new 异常类(...) 异常类的名字
口诀 真扔 报备
// throws 在头上报备,throw 在体内真扔
static void quQian(int money) throws IllegalArgumentException {
    if (money < 0) {
        throw new IllegalArgumentException("金额不能负");
    }
    System.out.println("取钱成功:" + money);
}

public static void main(String[] args) {
    try {
        quQian(-10);
    } catch (IllegalArgumentException e) {
        System.out.println("金额不合法");
    }
}

五、常见异常速查

运行时异常(最常见)

异常 什么时候出现
NullPointerException 调用 null 对象的方法
ArrayIndexOutOfBoundsException 数组下标越界
StringIndexOutOfBoundsException 字符串下标越界
ClassCastException 类型强制转换错误
ArithmeticException 算术错误,如整数除以 0
NumberFormatException 字符串转数字失败
IllegalArgumentException 参数不合法

编译时异常(必须处理)

异常 什么时候出现
IOException IO 操作失败
FileNotFoundException 文件找不到
SQLException 数据库访问错误
ClassNotFoundException 找不到类

六、总结

  1. 异常是运行时的错误,语法没问题,跑起来才出错;不处理就会崩溃,后面的代码不再执行。
  2. 所有异常都继承自 Throwable,分 Error(管不了)和 Exception(能处理)。
  3. Exception 又分编译时异常(必须处理)和运行时异常(运行才暴露)。
  4. 处理方式有三种:try-catch 自己捕获、throws 声明抛出、throw 手动抛出;资源关闭优先用 try-with-resources。
  5. throw 是真扔,throws 是报备;多个 catch 要把子类写在前面。
posted @ 2026-10-25 16:17  余子馨  阅读(7)  评论(0)    收藏  举报