异常概述

异常的作用:增强程序的健壮性。


java中异常以什么形式存在?

  1. 异常在java中以类的形式存在,每一个异常类都可以创建异常对象。
    JVM执行到某一处觉得有异常,会new异常对象,并且将new的异常对象抛出,打印输出信息到控制台了。如:new ArithmeticException(“/ by zero”);
  2. 异常对应的现实生活
    火灾(异常类)
    • 2008年8月8日,小明家着火了(异常对象)
    • 2008年8月9日,小红家着火了(异常对象)

异常体系结构

注意:所有异常都是在运行阶段发生的,因为只有程序运行阶段才可以new对象,因为异常的发生就是new异常对象。


编译时异常和运行时异常的区别:

  • 编译时异常一般发生的概率比较高。对于一些发生概率较高的异常,需要在运行之前对其进行预处理。比如,外面在下雨,如果不拿伞出去我可能会生病(异常),所以拿伞就是对“生病异常”发生之前的处理。
  • 运行时异常一般发生的概率比较低。对于一些发生概率较低的异常,没必要提前对这种发生概率较低的异常进行预处理。如果处理这种异常,你会活的很累。
  • 编译时异常也叫受检或受控异常:CheckedException;运行时异常也叫未受检或未受控异常:UnCheckedException。

java中处理异常有两种方式:

  • 第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。谁调用我,我就抛给谁。
  • 第二种方式:使用try…catch语句进行异常的捕捉。
  • 异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果:终止java程序。(方法1 –> 方法2 –> … –> JVM)

try…catch:

  • catch后面小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
  • catch可以写多个,从上到下,必须遵守从小到大。
  • 建议catch的时候,精确的一个一个处理,有利于程序的调试。

java8新特性:
可以这样书写:catch(FileNotFoundException | ArithmeticException | NullpointerException e) { … }


throws 和 try…catch怎么选?
如果希望调用者来处理,选择throws上报,其他情况使用捕捉的方式。


异常对象两个重要的方法:

  • 获取异常简单的描述信息:exception.getMessage();
  • 打印异常追踪的堆栈信息:exception.printStackTrace();

finally语句:
放在finally语句块中的代码是一定会执行(除了调用System.exit(0) 退出JVM),即使try中有return。如果try中有return,会先执行finally再return。


finally面试题:

public static int m() {int i = 100;try {return i;} finally {i++;}}

返回结果是:100。
问题:不是应该先执行finally语句进行 i++,再return出去吗?
解释java有一条语法规则,方法体中的代码必须遵循自上而下顺序依次逐行执行。第一行 i=100,第二行 return i,就应该返回100。如果 i=100,再 i++,再return,这样就违背了java的语法规则。但是finally还是执行了,这段代码的内部执行顺序是这样的:

结论在 finally 中改变返回值的做法是不好的。因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。

从JVM的角度分析:

public class Demo3_12_2 {public static void main(String[] args) {int result = test();System.out.println(result); // 20}public static int test() {try{return 10;} finally {return 20;}}}

public class Demo3_12_2 {public static void main(String[] args) {int result = test();System.out.println(result); // 10}public static int test() {int i = 10;try{return i;} finally {i = 20;}}}


Java中如何自定义异常:

  1. 编写一个类继承Exception或者RuntimeException。
  2. 提供两个构造方法,一个无参,一个带有String参数的。
    public class MyStackOperationException extends Exception {public MyStackOperationException () {}public MyStackOperationException () {super(s);}}/*public class MyStackOperationException extends RuntimeException{ //运行时异常}*/
    public void push(Object obj) throws MyStackOperationException {if(index >= elements.length - 1) {//注意:这里手动抛出异常,要throws出去,不要try...catch,自己抛自己捕获没有意义throw new MyStackOperationException("压栈失败,栈已满!");}}

注意:重写之后的方法不能比重写之前的方法抛出更多的异常,可以更少。