Java异常(Exception)讲解(/try/catch/throw/throws/finally关键字/异常处理流程/自定义异常)

欢迎访问作者Github:Joeysoda/Github_java: Study java

在 Java 中,异常是程序在执行过程中发生的非正常行为。理解并正确处理异常是编写健壮、可靠代码的关键。下面我们将详细讲解 Java 的异常处理机制,涵盖异常体系结构、异常的分类、异常的处理流程、以及关键字 trycatchthrowthrowsfinally 的使用。


 

目录

1. 异常的体系结构

1.1 Throwable 类

1.2 Error 类

1.3 Exception 类

1.4 异常的分类

2. 异常的处理

2.1 防御性编程策略

2.2 异常的抛出

2.3 异常的捕获

2.3.1 throws 声明

2.3.2 try-catch 捕获

2.3.3 finally 块

面试题:finally 和 try-catch-finally 后的代码都会执行,为什么还要有 finally?

3. 面试题解析

3.1 throw 和 throws 的区别

3.2 finally 中的语句一定会执行吗?

4. 自定义异常类

自定义异常注意事项:

总结:异常的处理流程


1. 异常的体系结构

1.1 Throwable

Java 中所有异常类都继承自 java.lang.Throwable,它是 Java 中异常体系的根类。Throwable 有两个直接子类:

  • Error:表示系统级错误,是 JVM 无法处理的严重问题,例如 StackOverflowErrorOutOfMemoryError。这些错误通常由 JVM 抛出,程序无法恢复或处理这些错误。
  • Exception:表示程序运行过程中可以预期并可以通过代码处理的异常,例如 IOExceptionNullPointerException 等。
1.2 Error
  • Error 类代表了 JVM 运行过程中出现的严重错误,程序员通常不需要或无法处理这些错误,属于系统级错误。
  • 典型的 Error
    • StackOverflowError:栈溢出错误。
    • OutOfMemoryError:内存溢出错误。
1.3 Exception
  • Exception 类是应用程序可以通过代码进行捕获和处理的异常类。典型的异常包括:
    • ArithmeticException:算术异常(如除以 0)。
    • ArrayIndexOutOfBoundsException:数组越界异常。
    • NullPointerException:空指针异常。
1.4 异常的分类
  • 编译时异常(Checked Exception)

    • 编译时异常是在代码编译阶段需要强制处理的异常。如果程序员没有正确处理这些异常,编译器会报错,无法通过编译。常见的编译时异常有 IOExceptionSQLException
    • 处理方式:需要通过 try-catchthrows 来处理。
  • 运行时异常(Unchecked Exception/RuntimeException)

    • 运行时异常是在程序运行时发生的异常,通常是由于编程错误引起的。这类异常不要求在编译时必须处理,程序可以选择不捕获此类异常,直接由 JVM 处理。常见的运行时异常包括 NullPointerExceptionArrayIndexOutOfBoundsException
    • 处理方式:可以不用强制处理,但建议通过良好的编程习惯来避免这些错误。

2. 异常的处理

2.1 防御性编程策略
  • LBYL(Look Before You Leap):事前检查。在执行操作之前,检查是否可能发生错误。这是一种预防性编程方法。

  • 示例:
    if (list != null && list.size() > 0) {
        System.out.println(list.get(0));
    }
    

    EAFP(Easier to Ask for Forgiveness than Permission):事后处理。这是一种容错性编程方法,不做过多检查,直接操作,若发生异常再进行处理。

  • 示例:
    try {
        System.out.println(list.get(0));
    } catch (NullPointerException e) {
        System.out.println("列表为空!");
    }
    
2.2 异常的抛出

在 Java 中,可以使用 throw 关键字手动抛出异常。常见的异常抛出场景包括参数校验失败等。

注意事项

  1. throw 必须写在方法体内
  2. 抛出的对象必须是 Throwable 的子类(ExceptionError)。
  3. 如果抛出的是 RuntimeException 或其子类,则可以不强制捕获或声明,由 JVM 处理。
  4. 如果抛出的是编译时异常(Checked Exception),调用者必须处理,否则无法通过编译。

示例

public void validateAge(int age) {
    if (age < 18) {
        throw new IllegalArgumentException("年龄必须大于18");
    }
}
2.3 异常的捕获

Java 提供了 try-catch 机制来捕获和处理异常。程序可以在 try 块中编写可能会抛出异常的代码,如果出现异常,就会跳到 catch 块中处理。

2.3.1 throws 声明

当一个方法内部可能抛出异常但不处理时,可以通过 throws 关键字将异常抛给上层调用者。

注意事项

  1. throws 必须跟在方法参数列表之后。
  2. throws 后的异常必须是 Exception 或其子类。
  3. 如果方法可能抛出多个异常,必须列出多个异常类型,或声明父类即可。

示例

public void readFile(String path) throws IOException {
    FileReader fileReader = new FileReader(path);
}
2.3.2 try-catch 捕获
  • try:包含可能发生异常的代码。
  • catch:捕获异常并处理。

示例

try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("除数不能为0");
}
2.3.3 finally
  • finally 块中的代码无论是否发生异常,都会执行,常用于资源清理操作。
  • 典型场景:关闭文件、释放数据库连接、关闭网络连接等。

示例

try {
    FileReader file = new FileReader("test.txt");
} catch (FileNotFoundException e) {
    System.out.println("文件未找到");
} finally {
    System.out.println("最终的资源清理操作");
}
面试题:finallytry-catch-finally 后的代码都会执行,为什么还要有 finally
  • finally 块的意义在于:无论 try 中是否抛出异常,finally 块的代码一定会执行。即使在 trycatch 块中发生 returnfinally 仍会被执行。
  • try-catch-finally 后的代码不一定能执行到,因为如果 trycatch 中有 return异常终止,后续代码就不会执行,但 finally 会被执行。

3. 面试题解析

3.1 throwthrows 的区别
  • throw:用于显式抛出异常。它是用于在方法内部手动抛出异常的。

throw new IllegalArgumentException("无效参数");
  • throws:用于声明异常,表示当前方法可能抛出异常,需要调用者处理。它用于方法声明中,放在参数列表后面。

3.2 finally 中的语句一定会执行吗?
  • 是的finally 中的代码通常会执行,无论是否抛出了异常。但在某些极端情况下,finally 中的代码可能不会执行,例如:
    • 程序在 trycatch 中调用了 System.exit() 退出 JVM。
    • JVM 崩溃,或系统中断。
    • 程序所在的线程被强制终止。

4. 自定义异常类

在 Java 中,程序员可以自定义异常类,通常用于封装业务逻辑中的异常信息。自定义异常类通常继承自 ExceptionRuntimeException

自定义异常注意事项
  1. 继承自 Exception 的异常是受检异常,调用者必须处理。
  2. 继承自 RuntimeException 的异常是非受检异常,调用者可以选择不处理,直接交给 JVM 处理。

示例:自定义异常类

class AgeException extends Exception {
    public AgeException(String message) {
        super(message);
    }
}

public void setAge(int age) throws AgeException {
    if (age < 18) {
        throw new AgeException("年龄不能小于18");
    }
}

总结:异常的处理流程

  1. 程序执行 try中的代码。
  2. 如果 try 中的代码出现异常,执行会停止,检查 catch 块中的异常类型是否匹配。
    • 如果匹配,执行 catch 中的代码。
    • 如果没有找到匹配的 catch,异常会继续向上层调用者传播。
  3. 无论是否匹配到异常类型,finally 块中的代码都会执行
  4. 如果异常没有被任何地方捕获,最终会传递到 main 方法,程序会异常终止。

通过正确的异常处理机制,程序可以更好地处理运行时的错误,保持稳定性和健壮性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值