OCA/OCP Java Note (11): Exceptions

1. Understanding Exception Types

  Java中异常的种类如图1所示。

图1

图1

  Error用于指示严重错误,程序不应尝试从错误中恢复。RuntimeException和其子类表示运行时异常,用于指示意外的非致命异常,也被称为unchecked exception。

  Runtime (Unchecked) exception是异常的一个种类,并不是指在程序运行期间(run time)发生的异常。所有异常都发生在程序运行期间,与之相对的是编译时(compile time),编译时可能会发生编译错误。

  Checked exception包括Exception和其没有继承RuntimeException的子类,对于checked exception,Java要求代码要么处理,要么在函数上声明(handle or declare rule)。

  以上内容总结如图2。

图2

图2

eg. 

2. Using a try Statement

2.1. Adding a finally Block

  finally总会执行,但有一种特例:如果在try或catch中调用了 System.exit() ,finally就不会执行。

2.2. Catching Various Types of Exceptions

  Java会按照在代码中出现的顺序检查各个catch,如果有不可能被执行到的catch,就会给出编译错误。比如在catch子类异常前catch了父类异常。

eg. 

2.3. Throwing a Second Exception

eg. 

第36行抛出NullPointerException,37行被跳过;40行抛出RuntimeException,41行后的finally仍会被执行。最后 result 为:

3. Recognizing Common Exception Types

3.1. Runtime Exceptions

  Runtime exception不强制要求被处理或声明,可由JVM或程序员抛出。

3.1.1. ArithmeticException

  尝试进行除以0的运算时由JVM抛出。

eg. 

抛出异常:

3.1.2. ArrayIndexOutOfBoundsException

  使用非法索引访问数组时由JVM抛出。

eg. 

抛出异常:

3.1.3. ClassCastException

  转换为子类,但对象并不是子类的实例时,由JVM抛出。编译器会首先对转换进行检查,更复杂的转换错误会在运行时抛出异常。

eg. Integer不是String的子类,下面的代码会给出编译错误:

下面的代码会抛出异常:

抛出异常:

3.1.4. IllegalArgumentException

  指示传入的参数不合法或不合适,由程序员抛出。

eg. 对于如下的代码:

调用 setNumberEggs(-2) ,会抛出:

3.1.5. NullPointerException

  访问对象时发现null引用,由JVM抛出。

eg. 

抛出异常:

3.1.6. NumberFormatException

  将字符转换为数值类型,字符格式不正确时,由程序员抛出。

eg. 

抛出异常:

3.2. Checked Exceptions

  Checked exception都可以由JVM或程序员抛出。

3.2.1. FileNotFoundException

  尝试引用不存在的文件时抛出,是IOException的子类。

3.2.2. IOException

  读写文件出现问题时抛出。

3.3. Errors

  错误继承Error类,由JVM抛出,不应被处理或声明。

3.3.1. ExceptionInInitializerError

  静态初始化器抛出异常且未处理时,由JVM抛出。

eg. 

抛出关于两个异常信息:

3.3.2. StackOverflowError

  方法调用自身次数过多(无限递归)时,由JVM抛出。

eg. 

抛出错误:

3.3.3. NoClassDefFoundError

  类的代码在编译时可用,而在运行时不可用时,由JVM抛出。

4. Calling Methods That Throw Exceptions

  Checked exception必须被处理或声明,否则会出现编译错误。

eg. 

上面的代码在 main() 中 eatCarrot() 的异常没有被处理,导致编译错误。以下两种 main() 可以正常编译:

  编译器会检查无法运行到的代码(unreachable code),声明但不抛出异常是合法的,但尝试catch没有抛出的异常会导致编译错误。

eg. 

4.1. Subclasses

  子类尝试重写父类方法或者实现接口方法时,不允许在函数签名上添加新的checked exception。

eg. 

  子类可以声明更少的异常

eg. 

  子类也可以声明抛出父类或者接口方法所抛出异常的子类。

eg. 

  上面的规则只适用于checked exception。子类可以声明新的runtime exception,因为对runtime exception的声明是冗余的,方法可以任意抛出任何runtime exception而不需要进行声明。

eg. 

4.2. Printing an Exception

  有三种方式可以打印出异常:让Java使用默认方式打印,打印异常消息,或者打印栈跟踪信息。

eg. 下面的例子使用这三种方式打印异常:

输出为: