1、异常简介
异常就是有异于常态,和正常情况不一样,有错误产生。在Java中,阻止当前方法或作用域的情况,称之为异常。Java中所有的异常类都继承自Throwable
类。
Thowable
类是Java语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过Java虚拟机或者Java throw
语句抛出,其实现子类包含Error
和Exception
。
Error
是Throwable
的子类,用于指示合理的应用程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件。在执行该方法期间,无需在其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
。
若是自定义异常类来处理特定问题,那么就需要自己完成发现异常、抛出异常以及处理异常的一整套工作,会增加一些开发成本和工作量,那么为什么会需要用到自定义异常呢?
-
当作为团队进行项目开发时,使用自定义异常类可以统一对外异常展示的方式。
-
有时候处理某些校验或者问题时需要直接结束掉当前请求,可以通过抛出自定义异常来结束。
-
可自定义某些符合Java语法但是不符合业务逻辑的“异常”。
-
可以隐藏底层的异常,这样更安全,异常信息也更加的直观。
5、Java的异常链
异常需要封装,但是仅仅封装还是不够的,还需要传递异常。、
异常链时一种面向对象编程技术,指将捕获的异常包装进一个新的异常中并重新抛出的异常处理方式。原异常被保存为一个新异常的属性(比如说cause)。这样做的意义时一个方法应该抛出定义在相同的抽象层次上的异常,但是不会丢弃更低层次的信息。
如果需要理解的话,大概是
-
方法a需要一个object-b来完成接下来的操作,所以它调用了方法b来生成,
-
但是方法b发生了异常b,无法完成操作并返回异常b。
-
那么方法a收到异常b,但是没收到object-b,无法完成操作,准备抛出异常a,抛出异常a的时候需要把收到的异常b封装进去一起抛出,不然上层程序无法弄清楚异常到底是为什么发生的。
或者说看看编程时调用方法出错后跳出的一连串的报错吧。
参考