业务背景
业务开发中,总会遇到代码出现异常的情况,不合理的异常处理或不处理异常除了影响业务功能和中断业务功能外,还会增加排查问题的难度。所以我们要学会正确的使用异常处理。合理的异常处理能减少很多潜在的问题,是提高代码鲁棒性的重要手段。
什么是异常
异常是程序在运行过程中发生的意外情况,通常是由外部因素或程序逻辑中的特殊情况引起的。例如文件不存在,网络连接中断,用户输入不合法等。和bug有所不一样,bug是指程序中存在的缺陷或错误,通常是由开发者的逻辑错误,疏忽或需求理解不准确导致的。它们都有可能导致程序出错。
异常的分类
在Java语言中,针对异常提供了异常处理机制,Java把异常分为两类,分别是受检异常和非受检异常。
- 受检异常:程序在编译期间出现的异常,如文件不存在、网络连接失败等。
- 非受检异常:又叫做运行时异常(RunTimeException),通常是程序在运行期间逻辑导致的,如空指针异常、数组越界等。
特性 | 运行时异常(非受检异常) | 受检异常(Checked Exception) |
---|---|---|
发生阶段 | 运行期间 | 编译期间 |
是否强制处理 | 否 | 是 |
典型例子 | NullPointerException 、ArrayIndexOutOfBoundsException | IOException 、SQLException |
原因 | 程序逻辑错误或外部条件 | 外部因素(如文件不存在、网络中断) |
异常的处理
针对这两种异常,java提供了异常处理机制,即使用throws、throw(用于手动抛出异常)和使用try-catch-finally。
- thorws:用于声明方法可能抛出的异常,向上抛出异常,即把异常丢给调用方处理。
- try-catch- finally:用于捕获并处理异常,在catch内部完成异常处理。finally块中的代码都会执行,通常用于释放资源。
例如下面的代码,使用了两种处理机制,一个是try-catch在方法中闭环,一个时throws抛出异常。
package com.example.demo;import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;/*** @author lijianxiong*/
public class NetWorkException {public static void main(String[] args) {exceptionDemoByTryCatch();}public static void exceptionDemoByTryCatch() {try {URL url = new URL("https://nonexistentwebsite.example");HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.connect();} catch (IOException e) {System.out.println("try-catch方法捕获异常,网络连接出现异常: " + e.getMessage());}}public static void exceptionDemoByThrows() throws IOException {URL url = new URL("https://nonexistentwebsite.example");HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.connect();}}
自定义异常
在实际开发中,当Java提供的Java异常类不满足业务需求时,比如要根据业务内容处理更加细致的异常,如要判断用户年龄,判断名字是否合法等。那么我们可以使用自定义异常。自定义异常通常会继承Exception类或者它的子类。
-
继承Exception成为受检异常:如果自定义类直接继承Exception类,那么它就是一个受检异常,它使用时必须显示的处理。例如定义一个用户年龄不合法的异常。
// 自定义受检异常 public class InsufficientBalanceException extends Exception {public InsufficientBalanceException(String message) {super(message);} }// 业务类 public class BankAccount {private double balance;public BankAccount(double balance) {this.balance = balance;}public void withdraw(double amount) throws InsufficientBalanceException {if (amount > balance) {throw new InsufficientBalanceException("余额不足,当前余额: " + balance);}balance -= amount;System.out.println("取款成功,剩余余额: " + balance);} }// 测试类 public class Main {public static void main(String[] args) {BankAccount account = new BankAccount(1000);try {account.withdraw(1500); // 尝试取款} catch (InsufficientBalanceException e) {System.out.println("捕获到异常: " + e.getMessage());}} }
-
**继承RunTimeException类的自定义非受检异常:**在编译时不强制要求处理,以下是一个示例:
// 自定义非受检异常 class CustomUncheckedException extends RuntimeException {public CustomUncheckedException(String message) {super(message);} }public class UncheckedExceptionExample {public static void methodWithUncheckedException() {throw new CustomUncheckedException("这是一个自定义非受检异常");}public static void main(String[] args) {// 这里可以不捕获异常,编译能通过methodWithUncheckedException(); } }
总结
- 自定义异常通过继承Exception和RunTimeException实现。
- 自定义异常可以更好的表示特定业务。
- 异常处理可以提供清晰的错误信息和异常链,方便调试和维护。