基础Java异常处理 Basic Exception handling in Java
Made by Mike_Zhang
Java主题:
1. Exception
在运行程序的时候,经常会遇到很多异常(Exception)被抛出,使得程序不受我们的控制,因此在设计代码的时候会经常考虑很多情况,如:
1 |
|
但是很多时候我们并不能考虑到所有的异常情况,或者不能表达出这种异常,这会导致某个步骤因为异常而没有完成。
当一个成员方法抛出异常时,此方法不会返回任何值,只会抛出一个包含了异常信息的对象,并且此方法会立即终止。调用次方法的代码不会继续执行,而会用一种异常处理机制来寻找能够处理此异常的异常处理方法。
此时程序应该做:
- 使程序回到一个安全的状态并让用户执行其他程序;或者,
- 使用户保存所有步骤并且温柔地终止次程序。
因此需要有一种异常处理方法来把产生异常的地方转移到某一能过处理此异常的异常处理器。
1.2 Exception classification
异常(Exception)对象所属的类是从Throwable
类继承来的,因此除了Java所定义的异常外,用户还可以通过继承来定义自己的异常。
以下为Throwable
类的层级关系图:
Throwable
类有两个子类,Error
和Exception
。
Error
类描述的是在运行时的内部错误和资源穷尽情况。程序员不该抛出这类对象的错误,对应措施很少。
Exception
类也有两个子类,IOException
和RuntimeException
。RuntimeException
的产生是由于程序员自己在编写代码时产生了错误。IOException
是由于客观因素产生的,如I/O的错误。
RuntimeException
包含:
不当的转型;
数组的out-of-bounds错误;
访问一个空指针(null pointer)等
IOException
包括:
在EOF(End of file)之后继续读取;
打开一个不存在的文件;
从一个不存在的类中寻找对象等
记住:“如果产生了一个RuntimeException
,这就是你的错。”
Java规定所有从Error
类或者RuntimeException
类继承来的Exception
对象都是Unchecked exception。其余的被称为Checked exception。
Java编译器会根据你提供的异常处理器去检查所有的Checked exception。
2. Throwing exception
2.1 Checked exception declaration
在Java中,成员方法在监测到其不能处理的情况后可以抛出异常。成员方法不仅仅可以返回值给编译器,也可以给编译器抛出异常。
此类方法语法如下:
1 |
|
这个方法说明在创建此对象时,不仅仅可以产生一个FileInput
对象,也可以在此对象产生错误的时候抛出一个FileNotFoundException
异常。如果在创建对象的过程中产生了错误,那次对象不会被初始化,只会抛出一个异常。同时,系统也会去寻找一个能处理FileNotFoundException
异常的异常处理器。
异常会在一下四种情况中被抛出:
- 引用一个会抛出Checked exception的方法,如上面的
FileInput
方法;- 当检测到异常时通过
throw
语句抛出Checked exception;- 在编写程序时产生了错误,如
RuntimeException
;- 运行程序时的客观环境错误,如JVM的错误。
当遇到到前两点抛出的错误时,必须有相对应的异常处理器去处理这些异常,因为可以抛出异常的方法都是有可能陷入死循环的,必须有相应的处理措施。
在以上例子中,使用了exception specification
去定义了一个可抛出异常的方法,语法如下:
1 |
|
当然,一个方法也可能抛出不止一种类型的异常,需要在定义时列出所有异常类型,并用逗号隔开:
1 |
|
总的来说,一方法必须声明其所有的可能抛出的Checked exception。Unchecked exception是你不能控制的 (Error) 或者是你应该避免的 (RuntimeExcwption)。如果一个方法没有全部定义出其可能抛出的Checked exception,编译器会报错。
2.2 Exception throwing
异常声明完成后,需要在方法体中抛出(throw)异常。
一般过程为:
- 寻找合适的异常类型;
- 创建此类异常的对象;
- 抛出异常对象。
首先需要寻找并定义异常的类型,语法如下:
1 |
|
或者:
1 |
|
再抛出异常,
结合到例子中:
1 |
|
EOFException()
同时有一个有参构造方法,参数为字符串类型,可以更好的的描述此异常的含义:
1 |
|
2.3 Create exception class
当遇到的异常类型并不在标准异常类中时,可以创建我们自己的异常类。只需要从父类Exception
或者其子类继承即可,例如:
1 |
|
FileChildException
子类一般会有一个默认的无参构造方法,以及一个有参的构造方法以表明此异常的具体信息。父类Throwable
的方法toString()
可以返回前面定义在有参构造方法里的信息。
3. Catching exception
如果一个异常被抛出后没有对应的处理措施,则包含其的程序就回被终止,并且在终端输出异常的类型以及stack trace。
为获取一个异常并提供异常处理方法,可以使用try-catch语句块,语法如下:
1 |
|
如果try语句块中抛出了定义在catch中类型的异常,则:
- 跳过try语句块中剩余的语句;
- 执行catch语句块中的语句。
如果try语句块中没有抛出任何异常,则catch语句块就会被跳过。
如果一个方法中的try语句块抛出了一个没有定义在catch中的异常,则立即跳出此方法,不会执行后面的语句。
例子如下:
1 |
|
read()
方法可能会抛出IOException
异常,一旦异常被抛出,程序就回跳过其后的while
循环,直接进入catch
中的语句,最后输出StackTrace
.
同时我们也可以直接把异常声明在方法头中,一旦方法体中有异常,直接让此方法抛出异常,并不用去catch,例如:
1 |
|
总的来说,明确该如何处理某一异常情况下使用try-catch方法,否则就把异常在方法头中声明,抛出给方法。
注意:
当一子类继承没有抛出异常的父类后,必须在子类方法中抓取所有的
check exception
,不允许在子类方法的方法头后使用throws
关键字声明异常。
也可以抓取多个exception,分开进行异常处理,例子如下:
1 |
|
也可以进行结合 (Java7):
1 |
|
但是使用这种结构的异常类之间不能有继承关系。
在抓取到异常后,也可以受用异常类的方法去获取异常对象的信息:
得到异常对象的更多信息:1
e.getMessage();
得到具体的错误信息,或者异常对象的所属类:1
e.getClass.getName();
retry
语句可以使try
语句块再次运行一遍,例子如下:
1 |
|
3.1 NDECC
当一个异常被抛出,程序就会寻找能够处理此异常的nearest dynamically enclosing catch clause(NDECC),例子如下:
1 |
|
讨论以下种情况:
statement
语句抛出了ET1
类的异常,并且处于c3的语句能够处理此异常,则进行处理;statement
语句抛出了ET2
类的异常,并且处于c3的语句不能够处理此异常,则处于c2的语句进行处理;statement
语句抛出了ET3
类的异常,并且处于c3的语句不能够处理此异常,则处于c1的语句进行处理;
注意:
若
ET1
类是ET
类的子类,则ET
类的catch clause
可以处理ET1
类的异常。
3.2 Rethrowing excepltion
重新抛出异常通常有两种情况:
1.改变异常的类型;
例子如下:
1 |
|
2.只记录异常,不改变异常的类型,再次抛出。
1 |
|
4. The finally statement
当一个方法抛出异常后,它就回终止运行之后的代码并跳出此方法。但有时需要此方法运行完成,此时就需要用到finally
关键字。
不管有无异常抛出,finally
语句块中的内容都会被执行。
看以下例子,来自C. S. Horstmann, Core Java. Boston: Pearson, 2019(优秀的Java参考书,十分推荐)
1 |
|
讨论以下几种情况:
- 没有抛出异常:
首先执行
try
语句块中的代码,再执行finally
语句块中的代码,最后执行finally
语句块后的第一行代码。顺序为1,2,5,6;- 抛出异常并被catch语句 获取:
首先执行
try
语句块中的代码,直到抛出异常的那行代码,其后的代码都会被跳过。再执行catch
语句块中的代码,最后执行finally
代码块中的代码。之后再分两类情况:catch代码块中没有 异常抛出:
在上述过程后,最后执行finally
语句块后的第一行代码。顺序为1,3,4,5,6;catch代码块中有 异常抛出:
catch语句块中只会运行到抛出异常的那行代码,之后的代码会被跳过。此异常被抛出后,会抛出至引用此方法的方法。顺序为1,3,5;- 抛出异常并没有 被catch语句获取:
首先执行
try
语句块中的代码,直到抛出异常的那行代码,其后的代码都会被跳过。再执行finally
代码块中的代码。异常被抛出至引用此方法的方法。 顺序为1,5。
6. Standard exception in Java
参考:
Exception (Java SE 11 & JDK 11) - docs.oracle.com
参考
B. Eckel, Thinking in java. Upper Saddle River, N.Y. Prentice Hall, 2014.
C. S. Horstmann, Core Java. Boston: Pearson, 2019.
M. Goodrich, R. Tamassia, and A. O’reilly, Data Structures and Algorithms in Java, 6th Edition. John Wiley & Sons, 2014.
“Java API Reference,” docs.oracle.com. https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Exception.html (accessed Oct. 08, 2021).
写在最后
本章只介绍了Java异常抛出的基本内容。如Rethrowing and Chaining Exceptions、try-with-Resources Statement、Stack Trace等内容会持续更新。
最后,希望大家一起交流,分享,指出问题,谢谢!
原创文章,转载请标明出处
Made by Mike_Zhang