【Java学习笔记】第八章 异常处理

异常继承架构

整体架构如下:

【Java学习笔记】第八章 异常处理_第1张图片

  • error:严重系统错误,如硬件层面错误、内存不足等问题,从编程角度无法解决,虽然可try-catch,但不建议,应传到JVM或仅日志记录。
  • 受检异常:IOException(FileNotFountException)等,受编译程序检查,必须try-catch,否则编译不通过。
  • 非受检异常:RuntimeException,可以通过编译,也可以try-catch。从程序的角度来讲,这类异常一般都可以通过代码来规避异常的出现。非受检异常不try-catch也可以自动向外传播。

多重捕捉,可以catch多个异常,异常之间不得有继承关系,否则会发生编译错误。

throw、throws的使用时机

先举个例子展示throw和throws的使用方法,示例代码如下,示例代码的主要功能是读取文件中的文字内容并转化为一个String对象。throw用于方法体中,通常为catch的程序块,但不限于catch,任何位置使用throw抛出异常,会直接跳离当前流程。throws用于声明方法后抛出受检异常。

    public static String readFile(String fileName) throws Exception{
        StringBuffer text = new StringBuffer();
        try{
            Scanner console = new Scanner(new FileInputStream(fileName));
            while (console.hasNextInt()){
                text.append(console.nextLine()).append("\n");
            }
        }catch (FileNotFoundException ex){
            //日志记录等其他操作
            throw ex;
        }
        return text.toString();
    }

注意,如果使用throw抛出受检异常E,则其所在的函数必须使用throws关键字抛出E或E的父类异常。对于非受检异常,编译程序不强制要求函数使用throws。

在继承中,父类throws多个异常,子类重新定义该方法时

  • 可以不生命throws任何异常
  • 可以throws父类方法声明的某个/某几个异常
  • 可以throws父类方法异常的子类
  • 不可以throws父类方法中未声明的其他异常
  • 不可以throws父类方法中声明的异常的父类

那什么时候该抓,什么时候该抛?以下给我我所理解的该抛出的情况

  • 当前程序不足以处理异常情况
  • 当前方法的调用方需要了解抛出的异常并做相应的处理时。比如需要准备好输入的前置条件等。

异常这里有很多理论描述都比较拗口,也很难理解。我们在设计程序可以通过继承来自定义受检异常和非受检异常。那么如何在受检异常和非受检异常进行选择?我们需要考虑,调用方是否在调用时可以处理这种异常,如果其无力处理,必须提前做好前置条件再调用,那么就应该选择非受检异常。书里举了一个我不知道的例子,这里记录一下,留个印象Hibernate2中的HibernateException为受检异常,但Hibernate3中就变成了非受检异常。

重装堆栈追踪

e.fillInStackTrace()方法:可以重新装填异常堆栈,将起点设置为重新抛出异常的地方。书中的示例代码如下

    static void a(){
        try{
            b();
        } catch (NullPointerException ex){
            System.out.println("抛出a异常");
            ex.printStackTrace();
            Throwable throwable = ex.fillInStackTrace();
            throw (NullPointerException)throwable;
        }
    }

    static void b(){
        c();
    }

    static void c(){
        String text = null;
        text.toUpperCase();
    }

    public static void main(String[] args){
        try {
            a();
        }catch (Exception e){
            System.out.println("抛出main异常");
            e.printStackTrace();
        }
    }

输出结果如下,可以看出通过函数a的处理,重新组装异常,main抛出的异常的起点位置为重新抛出异常的地方(a).

【Java学习笔记】第八章 异常处理_第2张图片

assert

断言——“assert”就像是由编译程序控制是否能够执行的if语句。语法如下,若boolean_expression为false则发生java.lang.AssertionError,如果采取第二个语法,会将detail_expression的结果显示出来,如果detail_expression是个对象则会调用此对象的toString()方法。

assert boolean_expression;
assert boolean_expression: detail_expression;

为什么说是由编译程序控制的呢?如果我们在执行java指令时,指定-enableassertions或者-ea就会启动断言指令,否则不执行,编译程序就会忽略这段代码。

finally

1、try模块无论有误异常,finally都会执行,如果try/catch中return了,会先执行finally的语句再返回。

2、自动关闭资源(Try-With-Resources),语法及示例如下,将打开资源的代码放到try后面的括号中,即使不用撰写专门的finally去关闭资源,改资源也能自动调用类的close方法被自动关闭。这是jdk7之后的程序蜜糖,通过反编译程序可以看出来,是编译程序自动将其编译成了finally语句。与一般的try相比,自动关闭资源可以不撰写catch语句,但是如果没有catch语句抓住异常,必须由其所在的函数将对应异常抛出。

    public static String readFile(String fileName) throws FileNotFoundException{
        StringBuffer text = new StringBuffer();
        try(Scanner console = new Scanner(new FileInputStream(fileName))){
            while (console.hasNextInt()){
                text.append(console.nextLine()).append("\n");
            }
        }
        return text.toString();
    }

如果有多个资源语句,它们之间使用分号“;”隔开,关闭时由后至前调用close方法关闭。

3、可以关闭资源(包括自动关闭资源)的对象都继承了java.lang.AutoCloseable,必须实现接口的close方法。

addSuppressed

若一个异常被catch后的处理过程引发另一个异常,通常会抛出第一个异常作为相应。第二个异常通过addSuppressed()方法记录在第一个异常中,再通过getSuppressed()方法获得之前被add进去的第二个Thowable对象。该方法再jdk7后才增加的。

部分课后题

自动关闭资源,虽然使用try,但由于不强制其使用catch,所以可以将它和捕获异常分开来看。try后的程序块可以没有异常,那么其所在的函数也就可以不用throws任何异常。

【Java学习笔记】第八章 异常处理_第3张图片

你可能感兴趣的:(Java,学习笔记)