编程语言-Java系列Java

[Java]浅谈Java的异常体系

2020-11-05  本文已影响0人  AbstractCulture

为什么需要异常

理想的情况下,程序是不会有BUG的。但是现实的情况是:处处都可能引发BUG,比如一个糟糕的输入、需要访问的资源不存在、网络出现抖动、服务器资源不足等等。这就要求我们的程序需要一个机制来解决以下问题:

  1. 报告当前的错误信息
  2. 中断,发生了错误的逻辑不向下执行代码
  3. 抛出一个信号,让程序对此做出判断进行进一步的处理。

Java设计了一个异常处理的错误捕获机制来解决这些问题。

异常层次结构

image.png

可以看到,所有的异常都是由Throwable继承而来,而在下一层分为了2个分支:ErrorException

Error类层次结构描述的是Java运行时系统的内部错误和资源耗尽错误。(如著名的OutOfMemoryErrorStackOverflowError),一旦出现了这种错误,马上系统会通知用户,并尽力让程序安全地终止,就无能为力了。也就是说,这是程序员难以去控制的(排除个别场景压力测试引发的)。

Exception其实也分为2个分支:

下面我们举几个例子:

  1. ClassCastException-错误的类型转换
  2. ArrayIndexOutOfBoundsException-数组越界访问
  3. NullPointerException-空指针
  4. NoSuchElementException-访问的元素不存在

举几个常见的例子:

  1. FileNotFoundException-尝试打开一个不存在的文件
  2. ClassNotFoundException-无法根据给定的字符串找到表示的类

可见,RuntimeException通常是可以通过程序员的编程能力去解决的。而对于非RuntimeException,这是不可预期的,因为谁会指定这个一个该存在的文件是否被人为地删除了呢,因此这是根据资源环境决定的.

unchecked异常与checked异常

Java语言规范将派生与Error类或者RuntimeException类的所有异常称为非检查异常,所有其他的异常称为受查异常。这是什么意思呢?也就是Java认为,OOM和RumtimeException这种都是程序运行起来才会出现的,这些应该由程序员本身去规避。而由于环境的不确定因素引发的异常,比如说IO异常,那么就需要主动去解决,你可以选择throw,或者try catch,但是你不能不处理,否则无法通过编译。

如何处理异常

throws是带有传递性的,也就是说如果方法调用链路是:A->B->C,此时C对异常声明了,那么A和B都要处理这个异常。可以选择继续向上抛出,或者try catch。如果最终都不处理,那么由对应的异常处理器进行处理。

public class ThrowsException {
    /**
     * 我们声明了一个不存在的文件,试图获取它的输出流,这时IDE会提示我们需要处理受检查的异常
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        File noExitsFile = new File("D:\\logs\\notExistFile");
        FileOutputStream fileOutputStream = new FileOutputStream(noExitsFile);
    }
}
    public static void main(String[] args){
        File noExitsFile = new File("D:\\logs\\notExistFile");
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(noExitsFile);
        } catch (FileNotFoundException e) {
            // 实际工作中,应使用自定义异常将此错误抛出
            e.printStackTrace();
        }
    }

如果程序中有使用到资源,应声明finally对资源进行释放,注意,不要在finally里面做return这种逻辑。

    public static void main(String[] args) {

        File noExitsFile = new File("D:\\logs\\notExistFile");
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(noExitsFile);
        } catch (FileNotFoundException e) {
            // 实际工作中,应使用自定义异常将此错误抛出
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(fileOutputStream)) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    Logger.getGlobal().info("An exception occurred when closing the stream");
                }
            }
        }
    }

使用try-with-resource来控制你的资源

这样写是否觉得非常啰嗦,Java提供了另一种方式来释放资源更加优雅->try-with-resource,但是要确保资源类是否实现了AutoCloseable接口

 try(Resource resource = new Resource()){
     // do Something
 }catch(IOException e){
     // handle Exception
 }

案例:

    public static void main(String[] args) {

        File noExitsFile = new File("D:\\logs\\notExistFile");
        try (FileOutputStream fileOutputStream = new FileOutputStream(noExitsFile)) {
            FileDescriptor fd = fileOutputStream.getFD();
        } catch (IOException e) {
            // 实际工作中,应使用自定义异常将此错误抛出
            e.printStackTrace();
        }
    }

多个资源如何声明:

    public static void main(String[] args) {

        File noExitsFile = new File("D:\\logs\\notExistFile");
        try (FileOutputStream fileOutputStream = new FileOutputStream(noExitsFile);
             FileInputStream fileInputStream = new FileInputStream(noExitsFile);) {
            FileDescriptor fd = fileOutputStream.getFD();
            FileDescriptor fd1 = fileInputStream.getFD();
        } catch (IOException e) {
            // 实际工作中,应使用自定义异常将此错误抛出
            e.printStackTrace();
        }
    }

处理异常的一些原则

上一篇 下一篇

猜你喜欢

热点阅读