JAVA篇:Java异常

1、异常简介

异常就是有异于常态,和正常情况不一样,有错误产生。在Java中,阻止当前方法或作用域的情况,称之为异常。Java中所有的异常类都继承自Throwable类。

  Thowable类是Java语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过Java虚拟机或者Java throw语句抛出,其实现子类包含ErrorException

  ErrorThrowable的子类,用于指示合理的应用程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件。在执行该方法期间,无需在其throws自居中声明可能抛出但是未能捕获的Error的任何子类,因为这些错误可能是再也不会发生的异常条件。一旦Error出现,程序就彻底的挂了,被称为程序终结者,包括了虚拟机错误和线程死锁。

  Exception类及其子类是Throwable的一种形式,它指出了合理的应用程序想要捕获的条件,也就是通常所说的“异常”。主要指编码、环境、用户输入操作出现问题。Exception主要包括两大类:非检查异常(RuntimeException)和检查异常(其他的一些异常)。

2、try-catch-finally语句

2.1 try语句块

负责捕获异常,一旦try块中发生了异常,程序的控制权将被移交给catch块中的异常处理程序。try语句块不可以独立存在,必须与catch或者finally块同存。

2.2 catch语句块

catch语句捕获相应的异常,进行发出警告、提示、检查配置、网络连接、记录错误等相应处理。catch块执行完毕之后跳出catch块,继续执行后面的代码。

当使用多个catch处理不同的异常类,要按照先catch子类后catch父类的处理方式,因为其处理异常的原则是:由上而下,就近处理

2.3 finally语句块

无论是正常执行try语句块结束还是移交异常处理catch块执行结束返回前,必须处理定义在finally中的语句。所以finally语句块中一般用于关闭和释放资源。

2.4 try-catch-finally流程和return

        int a=-1;
​
        try {
            System.out.println("in try");
            //a = 1/0; //当抛出异常时,转到catch语句
            a = 1;//若未抛出异常,继续调用finally中的语句
       
​
        }catch (Exception e){
            System.out.println("in catch");
            e.printStackTrace();//运行完毕后调用finally中的语句
        }finally {
            System.out.println("in finally");
​
        }
        System.out.println("finish");//catch块运行完毕后继续运行后续语句
        return a;

 

正常运行时的输出:

in try
in finally
finish
1

 

抛出异常时的输出:

java.lang.ArithmeticException: / by zero
in try
in catch
    at liwx.learning.Exceptest.test(Exceptest.java:14)
in finally
finish
    at liwx.learning.Exceptest.main(Exceptest.java:6)
-1

 

finally中return 覆盖问题:执行try结束(返回前--如果有return),记录返回值,去运行finally,如果此时finally中有return,则覆盖,catch同理。如下代码,返回的结果是3.

    try {
            System.out.println("in try");
            return 1;
​
        }catch (Exception e){
            System.out.println("in catch");
            e.printStackTrace();
            return 2;
        }finally {
            return 3;
        }

 


3、throw和throws关键字、自定义异常

throw关键字是一个抛出异常的动作,而throws用于声明某个方法可能会抛出的异常(可声明多种异常,用逗号分隔),然后交给上层调用该方法的程序处理。但是两者做的部分都是在发现异常后进行抛出(或声明),将异常的处理丢给上层处理。

//自定义异常
class MyException extends Exception{
    MyException(){
        super();
    }
    MyException(String str){
        super(str);
    }
}
//声明异常、抛出异常
public static void check(int a) throws MyException,Exception {
        if(a<0) {
            throw new  MyException("超出限制下限!");
        }
​
        if(a>1) {
            throw new  MyException("超出限制上限!");
        }
    }
 //调用方法并处理异常
    try {
            check(-1);
        }catch (MyException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }

 

前面自定义了一个异常类MyException

在Java中我们可以自定义异常,但是在编写自己的异常类的时候需要注意:

  • 所有的异常都必须是Throwable的子类。

  • 如果希望写一个检查性异常类,则需要继承Exception

  • 如果想写一个运行时异常类,那么需要继承RuntimeException

若是自定义异常类来处理特定问题,那么就需要自己完成发现异常、抛出异常以及处理异常的一整套工作,会增加一些开发成本和工作量,那么为什么会需要用到自定义异常呢?

  1. 当作为团队进行项目开发时,使用自定义异常类可以统一对外异常展示的方式

  2. 有时候处理某些校验或者问题时需要直接结束掉当前请求,可以通过抛出自定义异常来结束。

  3. 可自定义某些符合Java语法但是不符合业务逻辑的“异常”。

  4. 可以隐藏底层的异常,这样更安全,异常信息也更加的直观

5、Java的异常链

异常需要封装,但是仅仅封装还是不够的,还需要传递异常。、

异常链时一种面向对象编程技术,指将捕获的异常包装进一个新的异常中并重新抛出的异常处理方式。原异常被保存为一个新异常的属性(比如说cause)。这样做的意义时一个方法应该抛出定义在相同的抽象层次上的异常,但是不会丢弃更低层次的信息。

如果需要理解的话,大概是

  1. 方法a需要一个object-b来完成接下来的操作,所以它调用了方法b来生成,

  2. 但是方法b发生了异常b,无法完成操作并返回异常b。

  3. 那么方法a收到异常b,但是没收到object-b,无法完成操作,准备抛出异常a,抛出异常a的时候需要把收到的异常b封装进去一起抛出,不然上层程序无法弄清楚异常到底是为什么发生的。

或者说看看编程时调用方法出错后跳出的一连串的报错吧。

 

参考

 

Java异常类型及处理

JAVA基础——异常详解

Java API文档

上一篇:JavaSE——异常处理


下一篇:try..catch和finally