Java异常机制

1、异常概述

Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch子句中的参数类型。

两个子类的实例,ErrorException,通常用于指示发生了异常情况。

1.1异常分类:

异常包括Exception和Error,它们的特点如下:

  • Exception

    • 1.可以是受检(checked) 或非受检的(unchecked)。
    • 2.表示一个由程序代码导致的错误。
    • 3.应该在应用程序级被处理。
    • 4.分为编译时异常(必须处理)与运行时异常
  • Error

    • 1.总是不可控制的(unchecked)。
    • 2.经常用来用于表示系统错误或底层资源的错误。
    • 3.若可能的话,应该在系统级被捕捉。

受检异常和非受检异常
非受检异常(unchecked exception)指的是java.lang.RuntimeException和java.lang.Error类及其子类,除此之外的所有其他的异常类都称为受检异常(checked exception)。两者的唯一区别在于,使用受检异常时,其合法性在编译时刻就会被编译器检查。

1.2 继承体系

下图粗线条的展示了异常的体系:
Java异常继承体系


2、JVM的默认处理方案

  • 1.把异常的名称,错误原因及异常出现的位置等信息输出到控制台

  • 2.程序停止执行


3、编译时异常和运行时异常

Java中的异常被分为两大类:编译时异常运行时异常。在Exception继承体系下,所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常

  • 1.编译时异常
    Java程序必须显示处理,否则程序就会发生错误,无法通过编译

    扫描二维码关注公众号,回复: 153183 查看本文章
  • 2.运行时异常
    不必显示处理,也可以和编译时异常一样处理


4、异常(Exception)处理

4.1 处理方案

1、try…catch…finally (finally不是必须的)

注意细节

  • 1.try块出现异常处之后的语句不再执行,一旦发现有匹配的异常类型,就转到相应的catch块去执行相关逻辑,然后try…catch就结束了

  • 2.catch块可以捕获多种异常,但范围必须从小到大进行捕获,异常能明确的尽量明确,尽量不要用大范围的来处理。

  • 3.非特殊情况下,finally一定会执行,因此可以在该方法做一些资源回收的工作

示例1try {
            try块,正常执行的代码
        } catch (Exception e) {
            catch块,进行一场捕获,可以有多个catch块
        } finally {
            finally块,通常一定会执行,一般用于释放资源
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2、 throws(使用在方法声明上)

示例2:
        public void getBitmapFromUrl() throws IOException{

        }
  • 1
  • 2
  • 3
  • 4
  • 5

如果将异常抛出,那么调用者必须处理或抛出此异常

4.2 finally

4.21.finally的特点

  • 被finally控制的语句体一定会执行
  • 特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))。

注意:如果在执行到finally之前jvm退出了,就不能保证finally块执行了。

4.22.finally的作用

  • 用于释放资源,在IO流操作和数据库操作中会见到

4.23.final、finally和finalize的区别

final:最终的意思,可以修饰类,成员变量,成员方法

  • 1.修饰类,类不能被继承
  • 2.修饰变量,变量成为常量
  • 3.修饰方法,方法不能被重写

finalize:是Object类的一个方法,用于垃圾回收
finally:是异常处理的一部分,用于释放资源。

一般来说,代码肯定会执行。特殊情况:在执行到finally之前jvm退出了
  • 1
  • 2

思考:如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后?
会执行。并且在前。


5、Throwable中的方法

5.1.方法摘要

 Throwable getCause() 
          返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。 
 String getMessage() 
          返回此 throwable 的详细消息字符串。 

 String getLocalizedMessage() 
            创建此 throwable 的本地化描述。 子类可以重写此方法,以便生成特定于语言环境的消息。
            对于不重写此方法的子类,默认实现返回与 getMessage() 相同的结果。 
 StackTraceElement[] getStackTrace() 
          提供编程访问由 printStackTrace() 输出的堆栈跟踪信息。 
 void printStackTrace() 
          将此 throwable 及其追踪输出至标准错误流。 
 void printStackTrace(PrintStream s) 
          将此 throwable 及其追踪输出到指定的输出流。 
 void printStackTrace(PrintWriter s) 
          将此 throwable 及其追踪输出到指定的 PrintWriter。 
 String toString() 
          返回此 throwable 的简短描述。结果是以下字符串的串联:  
          1.此对象的类的 name 
          2.": "((冒号和一个空格) 
          3、调用此对象 getLocalizedMessage方法(默认与getMessage()返回相同的结果)的结果
        如果 getLocalizedMessage 返回 null,则只返回类名称
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

5.2. 使用示例

public class ExceptionDemo {
    public static void main(String[] args) {
        DateFormat formatter=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        try {
            String date = formatter.format(new Date("2016-1-1"));
            System.out.println(date);
        } catch (Exception e) {
            e.printStackTrace();
            //System.out.println(e.toString());
            //System.out.println(e.getMessage());
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6、区分throws和throw

6.1 throws

定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在方法上标识。特点如下:

  • 1.用在方法声明的方法名后面,跟的是异常类名
  • 2.可以声明抛出多个异常,后面跟多个异常类名,用逗号隔开
  • 3.表示抛出异常,由该方法的调用者来处理
  • 4.throws表示出现异常的一种可能性,并不一定会发生这些异常

6.2 throw

在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。特点如下:

  • 1.用在方法体内,表示抛出异常对象,且每次只能抛出一个异常对象
  • 2.能执行throw则一定抛出了某种异常,throw语句之后的代码不再执行

6.3 处理

  • 对于编译期异常,调用者必须处理。
  • 对于运行期异常,调用者可以不用处理。

6.4 异常注意事项

  • 子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。
  • 如果父类的方法抛出了多个异常,子类重写父类的方法时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常。
  • 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws

注意:此处所说的异常均为受检异常


7、自定义异常类

自定义异常类:要继承自一个异常类。 继承自Exception的异常为编译期异常,继承自RuntimeException的异常为运行期异常。
如下示例:

class CustomException extends Exception{

private static final long serialVersionUID = -2008123398676394901L;

    public CustomException(String msg) {
            super(msg);
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

8、JDK7对异常处理的新特性

8.1 处理多个异常

在jdk7中,可以在一个异常等级同时处理多个异常,形式如下:

try{

}catch(异常名1 | 异常名2 | ...  变量 ) {
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

示例

try {
         // to do
 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
         // deal with exception
 }
  • 1
  • 2
  • 3
  • 4
  • 5

注意事项:

  • 1 .处理方式是针对同类型的问题,给出同一个处理。

  • 2.多个异常间必须是平级关系。

8.2 addSuppressed

对于jdk之前的版本,如果在try块出了异常,同时当finally方法的逻辑也出了异常,那么会导致前面部分异常信息的丢失。即当finally方法异常被抛出的时候,会导致之前的异常因为该异常而被抑制住,从而无法正常抛出,造成了部分异常信息丢失。jdk7提供了addSuppressed方法把这些被抑制的异常信息记录下来。被抑制的异常会出现在抛出的异常的堆栈信息中,也可以通过getSuppressed方法来获取这些异常。使用addSuppressed方法可以让我们得到完整的异常堆栈信息,以方便开发调试。

如下示例:

 /**
     * @param input
     * @param def
     * @throws Exception
     */
    public static void useAddSuppressed(int input, String def) throws Exception {
        java.lang.Exception exception = null;
        int result = 0;
        try {
            result = 2 / input;
        } catch (ArithmeticException e) {
            exception = e;
            throw e;
        } finally {
            if (result == 0) {
                try {
                    result = Integer.parseInt(def);
                } catch (NumberFormatException e) {
                    if (exception != null) {
                        exception.addSuppressed(e); // 将信息添加到异常的堆栈信息
                    } else {
                        exception = e;
                    }
                }

                if (exception != null)
                    throw exception;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

当调用方法如下:

try {
        useAddSuppressed(0, "hello");
    } catch (Exception e) {
        e.printStackTrace();
    }
  • 1
  • 2
  • 3
  • 4
  • 5

输出异常信息如下,可见输出了完整的异常信息:

java.lang.ArithmeticException: / by zero
    at com.yu.kotlin.oop.ArrayTest.useAddSuppressed(ArrayTest.java:50)
    at com.yu.kotlin.oop.ArrayTest.main(ArrayTest.java:31)
    Suppressed: java.lang.NumberFormatException: For input string: "hello"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:580)
        at java.lang.Integer.parseInt(Integer.java:615)
        at com.yu.kotlin.oop.ArrayTest.useAddSuppressed(ArrayTest.java:57)
        ... 1 more
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

8.3 try-with-resources语句

Java 7中引入了使用try语句进行资源管理的新用法,简化try…catch…finally的结构,同时保证使用的资源可以得到释放。如下代码示例:

public static String readFile(String path) throws IOException {
     try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
         StringBuilder sb = new StringBuilder();
         String line = null;
         while ((line = reader.readLine()) != null) {
             sb.append(line);   // 添加内容
             sb.append(String.format("%n")); // 换行
         }
         return sb.toString();
     }
 // 不需要finally块来进行手动回收
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

资源的申请是在try子句中进行的,而资源的释放则是自动完成的。

使用使用try-with-resources语句异常抛出情况:

  • 异常可能发生在try语句,抛出try块相关异常
  • 可能发生在释放资源时,抛出资源释放相关异常
  • 上述两种同时发生,抛出try块相关异常,并将释放资源时出现的异常会作为被抑制的异常添加进去,即通过Throwable.addSuppressed方法来实现。得到所有异常信息。

注意
能够被try语句所管理的资源Java类需要满足一个条件,它必须实现java.lang.AutoCloseable接口,否则会出现编译错误。当需要释放资源的时候,其接口实现的close方法会被自动调用。

try-with-resources也可以对多个资源进行管理。当对多个资源进行管理的时候,在释放每个资源时都可能会产生异常。所有这些异常都会被加到资源初始化异常或try块中抛出的异常的被抑制异常列表中。

/**
  * 复制文件
  * @param src 源
  * @param dest 目的
  * @throws IOException
  */
 public static void copyFile(String src, String dest) throws IOException {
     try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(src));
          BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(dest))) {
         byte[] buffer = new byte[1024 * 4];
         int length = -1;
         while ((length = input.read(buffer)) != -1) { // read
             output.write(buffer, 0, length);  // write
         }
     }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在try-with-resource语句中也可以使用catch和finally语句,在catch子句中可以捕获try块和释放资源时可能发生的各种异常。

猜你喜欢

转载自blog.csdn.net/springyh/article/details/80209757