干了这么多年CRUD,你真的会处理异常吗?

发布时间:2023年12月28日

在这里插入图片描述

大家好,我是哪吒。

一、有很多小伙伴是这样处理异常的

  1. 每个方法都用trycatch处理异常;
  2. 在catch中记录异常日志,记录e.getMessage();
  3. 在finally中关闭io流;
  4. 在调用方法处,进行返回值判断,如果是false,表示出错,进行对应的业务逻辑处理;

因为没有系统的了解,只是知道异常的处理方式有,trycatch、throw、throws。具体什么情况用,也没去了解。使用trycatch也没啥问题,就一直这样了。

public void getData() {
    String path = "E:\\CSDN\\微服务\\图解Nginx,系统架构演变 + Nginx反向代理与负载均衡.md";
    boolean flag = readFile(path);
    if(flag){
        // 业务逻辑
        log.warn("读取文件"+path+"异常。");
    }
}

private boolean readFile(String path) {
    //创建 FileInputStream 对象,用于读取文件
    FileInputStream fis= null;
    try {
        fis = new FileInputStream(path);
        // 业务逻辑
    }catch (Exception e){
        log.error(".....异常:"+e.getMessage());
        return false;
    }finally {//关闭文件流,释放资源
        try {
            if (fis!=null) {
                fis.close();
            }
        } catch (IOException e) {
        }
    }
}

二、还有很多小伙伴是这样处理异常的

后来换了个项目,项目规定dao、service层的异常不要处理,要向上抛,然后在controller层做统一的trycatch处理,返回统一Result,根据业务需要去赋值,返回,这种方式也持续了很长一段时间。

在这里插入图片描述

大致如下:

@Controller
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger("UserController");

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/getUserById", method = RequestMethod.POST)
    @ResponseBody
    public Result getUserById(@RequestBody User user){
        Result result = new Result();
        try {
            User user = userService.getUserById(user.getId());
            result.setCode("200");
            result.setMsg("获取用户信息成功");
        } catch (Exception e) {
            result.setCode("500");
            result.setMsg("获取用户信息异常");
            logger.error("获取用户信息异常:",e);
        }
        return result;
    }
}
public interface UserService {
    User getUserById(Integer id) throws Exception;
}
@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getUserById(Integer id) throws Exception{
        // 业务逻辑
        return null;
    }
}
@Data
public class Result {
    // 状态码
    private String code;
    // 返回数据
    private String data;
    // 提示信息
    private String msg;
}

你也是这样吗?

三、Java异常简介

在这里插入图片描述
这部分之前整理过一篇文章,这里就不多赘述了。

【Java基础知识 6】Java异常详解

四、一些不好的异常处理方式

1、catch内不做任何处理

在写工具类的时候,因为某种原因,可能不会抛异常,或者出错了也没关系,也可能。。。,直接将其trycatch,catch内不做任何处理。

这么做是极其危险的,也是必须禁止的。

public void getCurrent(String time) {
    try {
        // 可复用逻辑
    }catch (Exception e){
    }
}

2、catch内通过e.getMessage()记录异常日志

只记录了异常消息,却丢失了异常的类型、栈等重要信息:

在这里插入图片描述

五、检查异常和非检查异常

检查异常,也可以称为“编译时异常”,编译器在编译期间检查的异常。如果抛出检查异常,那么编译器会报错,需要开发人员手动处理该异常,要么捕获,要么重新抛出。除了RuntimeException之外,所有直接继承 Exception 的异常都是检查异常。

非检查异常,也称为“运行时异常”,编译器不会检查运行时异常,在抛出运行时异常时编译器不会报错,当运行程序的时候才可能抛出该异常。Error及其子类和RuntimeException 及其子类都是非检查异常。

建议使用非检查异常让代码更加简洁,而且更容易保持接口的稳定性。

1、检查异常

检查异常(Checked Exceptions)是Java等编程语言中的一个概念。这些异常是编译器强制要求程序员进行处理的一类异常。如果程序员没有处理这类异常,编译器会报错,导致程序无法编译通过。

检查异常通常是由于编程错误或程序员处理数据时出错而引发的。例如,文件读写操作中可能出现的IOException、数据库操作中可能出现的SQLException等,都属于检查异常。

对于检查异常的处理,Java通常要求程序员使用try-catch语句块或者throws语句将其包装起来。其中,try-catch语句块用于捕获并处理异常,throws语句用于将异常传递给上级调用者。

虽然检查异常可以强制要求程序员进行处理,但也有人认为这种强制要求限制了程序员的编程自由度,因此有些新的编程语言(如Java 8之后的版本)开始逐渐减少或取消了检查异常的概念。

在这里插入图片描述

2、非检查异常

非检查异常(Unchecked Exceptions)是Java等编程语言中的一类异常,它们不需要编译器强制要求程序员进行处理。这类异常通常是由程序运行过程中出现的特定条件触发的,例如NullPointerException、IndexOutOfBoundsException等。

非检查异常是运行时异常的一种,它们的出现往往是由于程序员的编程错误或者程序运行环境的问题,例如数组越界、空指针访问等。这些异常发生时,Java虚拟机会自动抛出异常,并且不会强制要求程序员进行处理。

对于非检查异常,程序员可以选择在代码中进行捕获和处理,也可以选择忽略。例如,当出现NullPointerException时,程序员可以选择在代码中增加相应的判断来避免该异常的发生,或者选择忽略该异常继续执行程序。

虽然非检查异常不需要强制要求程序员进行处理,但是建议程序员在编写代码时尽可能地避免这类异常的发生,因为它们往往是由于编程错误或者程序运行环境的问题导致的,这些问题应该得到及时修正。

六、运行时异常与非运行时异常

运行时异常,也被称为非检查异常,是在Java中属于Exception类的子类。它们包括一些常见的异常,如:ClassCastException(类转换异常)、ClassNotFoundException(类未找到异常)、IndexOutOfBoundsException(数组越界异常)、NullPointerException(空指针异常)、ArrayStoreException(数组存储异常,即数组存储类型不一致)和BufferOverflowException(缓冲区溢出异常)等。这些异常通常在程序运行过程中由于某种特定的条件满足而自动触发。

非运行时异常,也被称为检查式异常,是Java中需要程序员手动处理的一种异常。这些异常包括IOException和SqlException等。对于这种类型的异常,程序员可以选择在方法签名中声明它们,并在方法体中进行捕获和处理,以便在发生异常时采取适当的措施。

七、try-with-resources

在上面的陈述中,有的小伙伴在finally中关闭io流,可是,在《阿里巴巴开发手册》中,推荐使用try-with-resources来关闭IO流。

JDK1.7开始,java引入了 try-with-resources 声明,将 try-catch-finally 简化为 try-catch,在编译时会进行转化为 try-catch-finally 语句,我们就不需要在 finally 块中手动关闭资源。

1、try-with-resources 声明包含三部分:

  1. try(声明需要关闭的资源);
  2. try 块;
  3. catch 块。

它要求在 try-with-resources 声明中定义的变量实现了 AutoCloseable 接口,这样在系统可以自动调用它们的close方法,从而替代了finally中关闭资源的功能,编译器为我们生成的异常处理过程如下:

  • try 块没有发生异常时,自动调用 close 方法;
  • try 块发生异常,然后自动调用 close 方法,如果 close 也发生异常,catch 块只会捕捉 try 块抛出的异常,close 方法的异常会在catch 中

通过调用 Throwable.addSuppressed 来压制异常,但是你可以在catch块中,用 Throwable.getSuppressed 方法来获取到压制异常的数组。

2、try-with-resources举例说明

try (ResourceType resource = new ResourceFactory().getResource()) {  
    // 使用资源  
} catch (Exception e) {  
    // 处理异常  
}

在上述代码中,ResourceType是资源的类型,ResourceFactory是一个可以创建资源的类。当try块执行完毕时,Java会自动调用resource.close()方法来关闭资源,即使在处理资源时抛出了异常。

如果你有一个需要关闭的文件资源,你可以这样写:

try (FileInputStream fis = new FileInputStream("file.txt")) {  
    // 使用文件输入流  
} catch (IOException e) {  
    // 处理异常  
}

在这个例子中,当try块结束时,Java会自动调用fis.close()方法来关闭文件输入流,即使在读取文件时抛出了异常。这样做可以确保即使发生异常,文件也能被正确地关闭,防止资源泄漏。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

文章来源:https://blog.csdn.net/guorui_java/article/details/133516468
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。