JAVA异常类 Throwable超类、Exception类、检查型异常、非检查型异常、异常处理方式、全局异常处理器、自定义异常、Result类

目录

1.异常(Throwable)  

1.1 Throwable 类的主要方法

1.2 Exception 类

1.2.1 异常处理

1.2.2 检查性和非检查性异常的不同点

1.2.3 异常处理关键字

1.2.3.1 try-catch捕获异常

1.2.3.2 多重捕获块

1.2.3.3 throws/throw 关键字

1.3 try-catch-finally

1.4 try-with-resources

1.4.1 释放单个资源

1.4.2 释放多个资源

2.全局异常处理器

2.1 全局异常处理器的实现

2.2 Result类封装

3.自定义异常

3.1 自定义检查型异常实现

3.2 使用自定义检查型异常

3.3 自定义非检查型异常实现

3.4 使用自定义非检查型异常

4.总结


1.异常(Throwable)  

Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。

1.Error错误(这种错误无法处理) 描述了Java运行时系统的内部错误和资源耗尽错误。一般是指虚拟机(JVM)相关的问题,如系统崩溃,虚拟机出错误等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常不处理。

2.Exception异常类又分为运行时异常(RuntimeException)和非运行时异常, 这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

因此只注重Exception异常类的处理。。。我们所说的异常处理都是处理Exception下的异常。

1.1 Throwable 类的主要方法

1.2 Exception 类

我们所说的异常处理都是处理Exception下的异常。

1.2.1 异常处理

要理解 Java 异常处理是如何工作的,你需要掌握以下两种种类型的异常:

1.检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这些异常在编译时强制要求程序员处理。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

解决:这类异常通常使用 try-catch 块来捕获并处理异常,或者在方法声明中使用 throws 子句声明方法可能抛出的异常。

注意事项:检查性异常强制要求程序员必须处理。

2.运行时异常(非检查时异常): 这些异常在编译时不强制要求处理,通常是由程序中的错误引起的,例如 NullPointerException、ArrayIndexOutOfBoundsException 、ArithmeticException、ClassCastException等。

解决:这类异常可以选择处理,但并非强制要求。

1.2.2 检查性和非检查性异常的不同点

  • 检查性异常的抛出

    • 如果方法可能会抛出检查性异常,必须在方法签名中使用throws关键字声明。
    • 例如:
      public void readFile() throws IOException {
          // 可能会抛出IOException的代码
      }
      
    • 调用这个方法的地方必须处理这个异常(使用try-catch)或者继续抛出。
  • 非检查性异常的抛出

    • 不需要在方法签名中声明抛出非检查性异常。
    • 例如:
      public void accessArrayElement(int[] array, int index) {
          if (index < 0 || index >= array.length) {
              throw new ArrayIndexOutOfBoundsException("Index out of bounds");
          }
      }
      
    • 调用这个方法的地方可以选择处理这个异常,但不强制要求

1.2.3 异常处理关键字

Java 提供了以下关键字和类来支持异常处理:

  • try用于包裹可能会抛出异常的代码块。
  • catch用于捕获异常并处理异常的代码块。
  • finally:用于包含无论是否发生异常都需要执行的代码块。释放资源。
  • throw用于手动抛出异常。通过手动创建异常对象。
  • throws用于在方法声明中指定方法可能抛出的异常。
  • Exception类:是所有异常类的父类,它提供了一些方法来获取异常信息,如 getMessage()、printStackTrace() 等。

1.2.3.1 try-catch捕获异常

使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。

使用 try/catch 的语法如下:

try
{
   // 程序代码
}catch(ExceptionName e1)
{
   //Catch 块
}

注意事项:当try出现异常代码,异常代码下面的代码都不会被执行。

1.2.3.2 多重捕获块

一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。

多重捕获块的语法如下所示:

try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型3 异常的变量名3){
  // 程序代码
}

执行说明:

上面的代码段包含了 3 个 catch块。

可以在 try 语句后面添加任意数量的 catch 块。

如果保护代码中发生异常,异常被抛给第一个 catch 块。

如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。

如果不匹配,它会被传递给第二个 catch 块。

如此,直到异常被捕获或者通过所有的 catch 块。

1.2.3.3 throws/throw 关键字

throw 关键字

throw 关键字用于在当前方法或者在catch代码块中手动抛出一个异常。

使用场景:自定义异常。。让报错信息更加有追踪性。。

代码:

public void checkNumber(int num) {
  if (num < 0) {
    throw new IllegalArgumentException("Number must be positive");
  }
}

throws 关键字

throws 关键字用于在方法声明中指定该方法可能抛出的异常。当方法内部抛出指定类型的异常时,该异常会被传递给调用该方法的代码,并在该代码中处理异常。

例如,下面的代码中,当 readFile 方法内部发生 IOException 异常时,会将该异常传递给调用该方法的代码。在调用该方法的代码中,必须捕获或声明处理 IOException 异常。

代码:

public void readFile(String filePath) throws IOException {
  BufferedReader reader = new BufferedReader(new FileReader(filePath));
  String line = reader.readLine();
  while (line != null) {
    System.out.println(line);
    line = reader.readLine();
  }
  reader.close();
}

注意事项:一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

1.3 try-catch-finally

使用 try-catch-finally 手动关闭资源。

代码演示:

import java.io.*;j

class RunoobTest {
    public static void main(String[] args) {
        BufferedReader br = null;
        String line;

        try {
            System.out.println("Entering try block");
            br = new BufferedReader(new FileReader("test.txt"));
            while ((line = br.readLine()) != null) {
            System.out.println("Line =>"+line);
            }
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        } finally {
            System.out.println("Entering finally block");
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                System.out.println("IOException in finally block =>"+e.getMessage());
            }
        }
    }
}

1.4 try-with-resources

try-with-resources 是一种异常处理机制,它能够自动关闭在 try 块中声明的资源,无需显式地在 finally 块中关闭。

在 try-with-resources 语句中,你只需要在 try 关键字后面声明资源,然后跟随一个代码块。无论代码块中的操作是否成功,资源都会在 try 代码块执行完毕后自动关闭。

语法:

try (resource declaration) {
  // 使用的资源
} catch (ExceptionType e1) {
  // 异常块
}

1.4.1 释放单个资源

演示优势:

使用 try-with-resources 自动关闭资源。

import java.io.*;

public class RunoobTest {

    public static void main(String[] args) {
    String line;
        // 这里的会自动执行br.close()
        try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            while ((line = br.readLine()) != null) {
                System.out.println("Line =>"+line);
            }
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        }
    }
}

1.4.2 释放多个资源

try-with-resources 语句中可以声明多个资源,方法是使用分号 ; 分隔各个资源:

import java.io.*;
import java.util.*;
class RunoobTest {
    public static void main(String[] args) throws IOException{
        try (Scanner scanner = new Scanner(new File("testRead.txt")); 
            PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            while (scanner.hasNext()) {
                writer.print(scanner.nextLine());
            }
        }
    }
}

2.全局异常处理器

在 Java 中,特别是在 Spring Framework 中,全局异常处理器是一种用于集中处理应用程序中发生的异常的机制。这种处理器可以帮助你管理和处理应用程序中的异常,从而提供统一的错误处理逻辑和用户友好的错误响应。

举个例子:

在Java Web项目中,尤其是采用了经典的三层架构(Controller、Service、Mapper)的项目中,异常处理是一个非常重要的环节。通常,当业务操作数据库出错时,异常会从持久层(Mapper)向上抛出,经过Service层,最终到达Controller层。为了确保前端能够正确处理这些异常,并且避免出现500错误,通常需要定义全局异常处理器来集中处理异常。

因此我们都会封装一个Result类加上全局异常处理器,可以友好的响应数据给前端。

而且不会出现状态码500的情况。。

解决方案:

  • 方案一:在所有Controller的所有方法中进行try…catch处理

    • 缺点:代码臃肿(不推荐)

  • 方案二:全局异常处理器

    • 好处:简单、优雅(推荐)

2.1 全局异常处理器的实现

我们该怎么样定义全局异常处理器?

  • 定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注解@RestControllerAdvice,加上这个注解就代表我们定义了一个全局异常处理器。

  • 在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。

注意事项:

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

处理异常的方法返回值会转换为json后再响应给前端。

在一个类上加@RestControllerAdvice就是全局异常处理器。

在异常处理器里面的方法加@ExceptionHandler就是处理的异常类型。此时就可以配合自定义异常使用。

代码演示:

//全局异常处理器
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Result ex(Exception ex){
        ex.printStackTrace();//打印堆栈中的异常信息
        return Result.error("出现错误信息,请解决");
    }
    
    //自定义异常(使报错更加详细清晰)
    @ExceptionHandler(Custom.class)
    public Result ee(Custom custom){
        log.error(custom.getMessage());
        return Result.error(custom.getMessage());
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {

    //处理异常
    @ExceptionHandler(Exception.class) //指定能够处理的异常类型
    public Result ex(Exception e){
        e.printStackTrace();//打印堆栈中的异常信息

        //捕获到异常之后,响应一个标准的Result
        return Result.error("对不起,操作失败,请联系管理员");
    }
}

全局异常处理器捕获到异常就会返回Result对象错误信息,里面封装成JSON格式之后,会被可以

被前端解析,并且前端会给出友好的回应,这说明前端可以解析该JSON数据。

2.2 Result类封装

在 Spring Boot 项目中,通常使用一个自定义的 Result 类来封装响应数据。这样的封装可以帮助统一返回数据的格式,方便前端处理和调试。以下是一个常见的 Result 类封装示例:

package com.lhx.entity;

import java.io.Serializable;

public class Result<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    private int code; // 响应码,用在error静态方法
    private String message; // 响应消息,用在error静态方法
    private T data; // 响应数据

    // 构造函数
    public Result() {
    }

    public Result(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 成功响应,不带数据
    // Result<T>这是方法的返回类型。
    // <T>: 这是泛型参数,表示这个方法可以处理任意类型的数据。
    // <T> Result<T>:第一个泛型是泛型方法,第二个泛型才可以使用到该泛型
    public static <T> Result<T> success() {
        return new Result<>(200, "操作成功", null);
    }

    // 成功响应,带数据
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }

    // 失败响应
    public static <T> Result<T> error(int code, String message) {
        return new Result<>(code, message, null);
    }

    // Getter 和 Setter 方法
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

3.自定义异常

在 Java 中,自定义异常是创建应用程序中用于特定错误处理的一种常见做法。

自定义异常可以提供更具体的错误信息,帮助在捕获和处理异常时进行更细粒度的控制。

3.1 自定义检查型异常实现

下面是一个自定义检查型异常的示例,继承自 Exception

// 自定义检查型异常
public class MyCheckedException extends Exception {
    // 默认构造函数
    public MyCheckedException() {
        // 调用父类的无参构造器
        super();
    }

    // 带有错误信息的构造函数
    public MyCheckedException(String message) {
        // 调用父类的有参构造器
        super(message);
    }

    // 带有错误信息和异常原因的构造函数
    public MyCheckedException(String message, Throwable cause) {
        // 调用父类的有参构造器
        super(message, cause);
    }

    // 带有异常原因的构造函数
    public MyCheckedException(Throwable cause) {
        // 调用父类的有参构造器
        super(cause);
    }
}

3.2 使用自定义检查型异常

在代码中使用自定义检查型异常时,需要在方法签名中声明抛出该异常,并在调用该方法时处理异常:

public class Example {
    public void doSomething() throws MyCheckedException {
        // 可能会抛出 MyCheckedException 的代码
        throw new MyCheckedException("Something went wrong!");
    }

    public static void main(String[] args) {
        Example example = new Example();
        try {
            example.doSomething();
        } catch (MyCheckedException e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }
}

3.3 自定义非检查型异常实现

下面是一个自定义非检查型异常的示例,继承自 RuntimeException

// 自定义非检查型异常
public class MyRuntimeException extends RuntimeException {
    // 默认构造函数
    public MyRuntimeException() {
        super();
    }

    // 带有错误信息的构造函数
    public MyRuntimeException(String message) {
        super(message);
    }

    // 带有错误信息和异常原因的构造函数
    public MyRuntimeException(String message, Throwable cause) {
        super(message, cause);
    }

    // 带有异常原因的构造函数
    public MyRuntimeException(Throwable cause) {
        super(cause);
    }
}

3.4 使用自定义非检查型异常

与检查型异常不同,非检查型异常不需要在方法签名中声明,可以直接抛出:

public class Example {
    public void doSomething() {
        // 可能会抛出 MyRuntimeException 的代码
        throw new MyRuntimeException("Something went wrong!");
    }

    public static void main(String[] args) {
        Example example = new Example();
        try {
            example.doSomething();
        } catch (MyRuntimeException e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }
}

通过定义继承自 Exception 或 RuntimeException 的类,你可以创建具有明确语义和额外上下文的异常,帮助你更好地管理和调试程序中的错误。

4.总结

全局异常处理器通常用于集中处理程序中可能抛出的所有异常,而自定义异常则用于定义和识别特定类型的错误情况。

从下面代码可以看出,基于web开发,接收到错误信息之后,返回给前端的是一个Result对象对应的JSON格式,该JSON可以被前端解析。如果没有进行统一对象处理,框架也会返回一个默认错误的JSON格式给前端,但是前端并解析不了这个默认JSON。

//全局异常处理器
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Result ex(Exception ex){
        ex.printStackTrace();//打印堆栈中的异常信息
        return Result.error("出现错误信息,请解决");
    }
    
    //自定义异常(使报错更加详细清晰)
    @ExceptionHandler(Custom.class)
    public Result ee(Custom custom){
        log.error(custom.getMessage());
        return Result.error(custom.getMessage());
    }
}
 public void logInfo(String useName,String passWord) throws UseNameException,PassWordException {
        if(!this.useName.equals(useName)){
            throw new UseNameException("用户名错误");
        }
        if(!this.passWord.equals(passWord)){
            throw new PassWordException("密码错误");
        }
        System.out.println("登陆成功");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值