Java的异常处理体系详解

 更新时间:2024年07月05日 09:29:48   作者:家乡的落日  
这篇文章主要介绍了Java的异常处理体系,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

异常处理

1、Java的异常类层次结构

其中Error表示程序运行错误

常见的错误类型有:

  • OutOfMemoryError (内存溢出错误)
  • StackOverFlowError (栈内存溢出错误)
  • IOError (IO错误)

Exception表示程序本身可以处理的异常

其中Exception又分为

①、CheckedException (检查异常 必须在代码中显式处理 使用try catch捕获 或者 在方法上使用 throws 抛出 除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 )

常见的包括:

  • IO Exception
  • SQL Exception
  • FileNotFoundException
  • ClassNotFoundException

②、UncheckedException(非检查异常 不用显式捕获 或者 throws抛出 )

常见的包括:

  • NullPointerException(空指针异常)
  • ClassCastException (类型转换错误)
  • IndexOutOfBoundsException (数组下标越界)
  • ConcurrentModificationException(并发修改异常)
  • NumberFormatException(数字转换异常)
  • ArithmeticException(算数异常)
  • IllegalArgumentException(参数错误)
  • UnsupportedOperationException(不支持的操作 比如使用Arrays.asList生成的集合 无法使用增删改操作)

2、try-catch-finally 使用注意事项

①、try块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。

②、当在try 块 或 catch块遇到 return语句时 finally 块会在 方法返回之前执行

③、不要在 finally 块中使用 return 语句 ,当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。

示例:

public int foo() {
    try {
        // some code that may throw an exception
        return 1;
    } catch (Exception e) {
        // handle exception
        return 2;
    } finally {
        return 3;
    }
}

在这个例子中,无论 try 块中的代码是否抛出异常,finally 块中的 return 3; 语句都会执行,而且会覆盖 try 块和 catch 块中的 return 语句,导致 foo() 方法始终返回3;

④、finally 块中的代码不一定会被执行 ,如果在try 或者 catch块中 出现了内存溢出或者jvm退出( System.exit(1);)等错误情况 ,对应finally 块中的代码就不会执行了。

⑤、面对必须要关闭的资源,我们总是应该优先使用 try-with-resources 而不是try-finally

try-with-resources 适用的资源包括 任何实现了 java.lang.AutoCloseable或者 java.io.Closeable 的对象

比如:Java 中类似于InputStream、OutputStream这类IO资源 需要我们使用完毕后手动关闭资源的

//读取文本文件的内容  BufferedReader 需要在使用完毕后手动释放资源
public static void main(String[] args) {
        String fileName = "C:\\Users\\Administrator\\Desktop\\数据分片.txt";
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(fileName))
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line); // 处理每一行的内容
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


//读取文本文件的内容  BufferedReader 在使用完毕后自动释放资源 (Java 7 之后支持try-with-resources )
public static void main(String[] args) {
        String fileName = "C:\\Users\\Administrator\\Desktop\\数据分片.txt";
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line); // 处理每一行的内容
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


//读取文本文件的内容  如果有多个资源同时声明 可以使用分号分隔即可
public static void main(String[] args) {
        String fileName = "C:\\Users\\Administrator\\Desktop\\数据分片.txt";
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName)); Scanner scanner = new Scanner(new File(fileName));) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line); // 处理每一行的内容
            }

            System.out.println("=====>>>>>>>>>>>>>>>>>>>>>>>>");
            while (scanner.hasNext()) {
                System.out.println(scanner.nextLine()); // 处理每一行的内容
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

⑥、不要省略异常信息的记录 在catch块中 使用日志记录关键异常信息

记录信息时尽量只写一个log.error或者log.info语句 , 因为在多线程环境下 两个相邻的日志语句不一定打印在相邻的位置

public void aa() {
    try {
    int a = 1/0;
    } catch (Exception e) {
    // 这里一定要记录完整的异常信息
      log.error("哦,错误竟然发生了: {}" , e);
    }
}

3、在Web应用中如何实现全局异常处理机制

主要利用 @ControllerAdvice或者@RestControllerAdvice注解

@ControllerAdvice:

@ControllerAdvice 注解用于定义全局控制器建议,在 Spring MVC 中,控制器建议由控制器中的 @ExceptionHandler 方法、@InitBinder 方法和 @ModelAttribute 方法组成。通过 @ControllerAdvice 注解的类,可以将这些方法集中到一个地方,以便对所有控制器进行统一的异常处理、数据绑定等操作。

@RestControllerAdvice:

@RestControllerAdvice 是 @ControllerAdvice 的一个变种,用于对以 REST 风格提供 API 服务的控制器进行全局建议定义。与 @ControllerAdvice 类似,@RestControllerAdvice 注解的类可以包含全局异常处理、全局数据绑定等方法,但它专门用于 RESTful 服务,可以将异常信息转化为 JSON 格式返回给客户端。

总的来说,@ControllerAdvice 用于传统的基于视图的控制器,而 @RestControllerAdvice 用于 RESTful 服务的控制器

一般情况下 建议在Web应用中捕获自定义异常 在自定义异常中抛出自定义错误代码 并且返回给前端 方便快速排查问题 或者提示用户相关业务的错误信息

①、利用枚举类型 自定义错误代码

/**
 * 自定义错误类型
 * */
public enum MyErrorCode {

    EMPTY_PARAM_ERROR("EMPTY_PARAM_ERROR", "远程调用错误"),
    REMOTE_ERROR("REMOTE_ERROR", "远程调用错误"),
    ;


    private final String code;
    private final String text;

    MyErrorCode(String code, String text) {
        this.code = code;
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public String getCode() {
        return code;
    }

}

②、自定义异常 继承自RuntimeException

/**
 * 自定义异常
 * */
public class MyException extends RuntimeException {

    /**
     * 错误代码
     */
    private String errorCode;

    /**
     * 格式化异常参数
     */
    private List<String> paramList;

    /**
     * 最常用的方法
     * @param MyErrorCode 自定义错误代码
     */
    public MyException(MyErrorCode MyErrorCode) {
        this(MyErrorCode.getCode(), MyErrorCode.getText(), null);
    }

    public MyException(String errorCode, String message) {
        this(errorCode, message, null);
    }

    private MyException(String errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }

    public MyException(MyErrorCode MyErrorCode, Throwable cause) {
        this(MyErrorCode.getCode(), MyErrorCode.getText(), cause);
    }

    public MyException(MyErrorCode MyErrorCode, List<String> params) {
        this(MyErrorCode.getCode(), MyErrorCode.getText(), null);
        this.paramList = params;
    }

    public List<String> getParamList() {
        return paramList;
    }

    public void setParamList(List<String> paramList) {
        this.paramList = paramList;
    }

    public String getErrorCode() {
        return this.errorCode;
    }
}

③、异常处理类

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;


@Slf4j
@RestControllerAdvice
public class MyExceptionHandle {


    @ExceptionHandler(MyException.class)
    public JsonResult<?> handleMyException(MyException e) {
        String message = e.getMessage();
        log.error("current web request error:" + message, e);
        JsonResult<Object> jsonResult = new JsonResult<>(false, message);
        String errorCode = e.getErrorCode();
        jsonResult.setErrCode(errorCode);
        List<String> paramList = e.getParamList();
        jsonResult.setData(paramList);
        return jsonResult;
    }


    /**
     * 处理未知异常
     *
     * @return JsonResult
     */
    @ExceptionHandler(Exception.class)
    private JsonResult<?> handleUnKnownException(Exception e) {
        if (null == e || StringUtils.isBlank(e.getMessage())) {
            log.error("current web request error:", e);
            return new JsonResult<Object>(false, "500", "unknownException", e);
        }
        String message = e.getMessage();
        log.error("current web request error:" + message, e);
        e.printStackTrace();
        JsonResult<Object> jsonResult = new JsonResult<>(false, "500", message, e);
        jsonResult.setMsg("unknownException");
        return jsonResult;
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中使用开源库JSoup解析HTML文件实例

    Java中使用开源库JSoup解析HTML文件实例

    这篇文章主要介绍了Java中使用开源库JSoup解析HTML文件实例,Jsoup是一个开源的Java库,它可以用于处理实际应用中的HTML,比如常见的HTML格式化就可以用它来实现,需要的朋友可以参考下
    2014-09-09
  • 详解Java动态代理的实现机制

    详解Java动态代理的实现机制

    这篇文章主要为大家详细介绍了Java动态代理的实现机制,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • 详解mybatis 批量更新数据两种方法效率对比

    详解mybatis 批量更新数据两种方法效率对比

    这篇文章主要介绍了详解mybatis 批量更新数据两种方法效率对比,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-02-02
  • Idea里github的图形化操作配置方法

    Idea里github的图形化操作配置方法

    这篇文章主要介绍了Idea里github的图形化操作配置方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • 浅谈Java slf4j日志简单理解

    浅谈Java slf4j日志简单理解

    这篇文章主要介绍了浅谈Java日志简单理解,详细的介绍了slf4j的概念和使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • SpringBoot不读取bootstrap.yml/properties文件问题

    SpringBoot不读取bootstrap.yml/properties文件问题

    这篇文章主要介绍了SpringBoot不读取bootstrap.yml/properties文件问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Android Studio更改项目使用的JDK(详细步骤)

    Android Studio更改项目使用的JDK(详细步骤)

    本文介绍了如何在Android Studio中修改Gradle和JDK的配置步骤,包括打开设置、进入Gradle设置、修改JDK路径、保存并生效等,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • Springmvc实现文件上传

    Springmvc实现文件上传

    这篇文章主要为大家详细介绍了Springmvc实现文件上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • 29个要点帮你完成java代码优化

    29个要点帮你完成java代码优化

    本文给大家分享的是个人总结的29个java优化需要注意的地方,非常的全面细致,推荐给大家,有需要的小伙伴可以参考下
    2015-03-03
  • java集合之CopyOnWriteArrayList源码解析

    java集合之CopyOnWriteArrayList源码解析

    这篇文章主要介绍了java集合之CopyOnWriteArrayList源码解析,容器array是volatile修饰的,即set和get方法都是线程安全的,整个添加过程上了锁,所以整体是通过volatile和lock来保证的线程安全,需要的朋友可以参考下
    2023-12-12

最新评论