6 异常处理
Head First Java 和 AcWing Java课程做的总结6。
当在编写可能有异常的方法时,需要处理异常状况的程序。
执行期的大多数问题来自程序的错误,而这些错误应该在开发阶段解决掉,但是某些错误还是有可能在执行期出现,例如找不到文件、服务器出现故障。
6.1 Error与Exception的区别
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。
6.2 异常处理(exception-handing)机制
-
是个简捷、轻量化的执行期间例外状况处理方式,它能够将处理错误状况的程序代码摆在一个容易阅读的位置。
-
需要知道所调用的方式是由风险的,也就是方法可能产生异常。看该方法的声明是否有
throws语句就知道了。 -
try-catch块-
把有风险的程序代码包含在
try-catch块中,编译器会知道所调用的方法有风险,并且也已经准备好处理它。 -
try{ //把有风险的程序放在try中 }catch(Exception ex){ //处理异常的程序 }
-
-
try-catch-finally块-
finally块是用来存放不管有没有异常都得执行的程序。 -
如果
try或catch块有return指令,finally还是会执行。 -
try{ //把有风险的程序放在try中 }catch(Exception ex){ //处理异常的程序 }finally{ //都得做的事 }
-
-
duck异常- 如果调用服务的程序
b不想处理异常,可以把它duck掉来避开。让调用程序b的程序a来catch该异常。没有用try-catch处理有风险的方法,则调用方b也成有风险的了。 - 只需要让程序
b声明成再throws此异常即可。 - 方法抛出异常时,方法会从栈上立即被取出,而异常会再度丢给栈上的方法,也就是调用方。
duck只是在踢皮球,早晚得有人来处理这件事,若main()也duck掉异常,则Java虚拟机直接死机。
- 如果调用服务的程序
-
异常处理规则:
catch与finally不能没有try;try一定要有catch或finally,即不能只有try;try与catch之间不能有程序;- 只带有
finally的try必须要声明异常。
6.3 异常对象
异常是一种Exception类型的对象。
- throw: 在函数内抛出一个异常。
- throws:在函数定义时抛出一些可能的异常。
程序代码会抓住异常,而抛出异常的是——声明有异常的方法,也就是该方法把异常抛出来。
方法可以抓住其他方法所抛出的异常。异常总是会丢给调用方。
举例:
//有风险、会抛出异常的程序代码
public void takeRisk() throws BadException{
if(abandonAllHope){
throw new BadException();
}
}
//调用该方法的程序代码
public void crossFinger(){
try{
anObject.takeRisk();
}catch(BadException ex){
System.out.println("Aaargh");
ex.printStackTrace();
}
}
内置异常方法:
| 方法 | 说明 |
|---|---|
public String getMessage() |
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了 |
public Throwable getCause() |
返回一个 Throwable 对象代表异常原因。 |
public String toString() |
返回此 Throwable 的简短描述。 |
public void printStackTrace() |
将此 Throwable 及其回溯打印到标准错误流 |
public StackTraceElement [] getStackTrace() |
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
public Throwable fillInStackTrace() |
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
6.4 异常分类

异常继承树:

不检查异常:
- 上图灰色,都继承自
java.lang.RuntimeException RuntimeException被称为不检查异常,可以自己抛出来并抓住它们,但是没有这个必要,编译器也不管。- 大部分的
RuntimeException都是因为程序逻辑的问题,而不是以无法预测或防止的方式出现的执行期失败状况。不检查异常一般是程序代码写的不够严谨而导致的问题,可以通过修改代码来规避。 try-catch是用来处理真正的异常。而不是程序的逻辑错误,该快要做的事恢复的尝试,或者至少列出错误信息。- 任何继承过
RuntimeException的类都不会受编译器是否声明它会抛RuntimeException的检查,同样的,也不会关调用方是否认识到可能会在运行期间遇到异常。 - 常见的运行时异常:空指针异常
(NullPointerException)、除零异常(ArithmeticException)、数组越界异常(ArrayIndexOutOfBoundsException)等;
检查异常:
- 上图白色,都继承自·
java.lang.Exception - 所有不是
RuntimeException派生的Exception都是检查型异常。 - 编译器所关心得异常,程序必须要意识到这种异常可能的存在。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
- 检查性异常必须使用try catch或者throws等关键字进行处理,否则编译器会报错;
- 常见的检查异常:输入输出异常
(IOException)、文件不存在异常(FileNotFoundException)、SQL语句异常(SQLException)等。
总的来说:
-
检查性异常: 不处理编译不能通过;
非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台。
-
运行时异常: 就是非检查性异常
非运行时异常: 就是检查性异常
6.5 内置异常类
非检查性异常:
| 异常 | 描述 |
|---|---|
ArithmeticException |
当出现异常的运算条件时,抛出此异常。例如,一个整数”除以零”时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException |
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException |
试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException |
当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException |
抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException |
抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException |
在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException |
线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException |
指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException |
如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException |
当应用程序试图在需要对象的地方使用 null 时,抛出该异常。 |
NumberFormatException |
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException |
由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException |
此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException |
当不支持请求的操作时,抛出该异常。 |
检查性异常:
| 异常 | 描述 |
|---|---|
ClassNotFoundException |
应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException |
当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException |
拒绝访问一个类的时候,抛出该异常。 |
InstantiationException |
当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException |
一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException |
请求的变量不存在 |
NoSuchMethodException |
请求的方法不存在 |
6.6 多重异常&异常多态
方法可以抛出多个异常,但该方法的声明必须要有含有全部可能的检查异常(若存在共同父类,则声明父类即可)。
处理多重异常:
-
要注意
catch出现的先后顺序; -
public class Laundry{ public void doLaundry() throws PantsException, LingerieException{ } } public class Foo{ public void go(){ Laundry laundry = new Laundry(); try{ laundry.doLaundry(); }catch(PantsException pex){ }catch(LingerieException lex){ } } } -
有多个
catch块时,要从小排到大。即异常继承树中越底层,越在catch中先出现。大的放在上面会导致无法通过编译。
异常的多态性:
-
异常也是对象,所以也能够以多态的方式来引用;
-
这样不必明确的声明每个可能抛出的异常,可以只声明父类即可。
-
//以异常的父型来声明会抛出的异常 public void doLaundry() throws ClothingException{ //声明父类异常可让抛出任何子类异常 } //以抛出异常父型来catch异常 ry{ laundry.doLaundry(); }catch(ClothingException cex){ } -
但是能够用父型来这么做,不代表就一个这么做
- 全部异常都可以用
catch(Exception ex)这样处理,但是这样完全搞不清哪里出错。
- 全部异常都可以用
6.7 try-with-resources
JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。
import java.io.*;
public class Main {
public static void main(String[] args) {
String line;
try (
BufferedReader br = new BufferedReader(new FileReader("input.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
) {
while ((line = br.readLine()) != null) {
System.out.println("Line => " + line);
bw.write("copy: " + line + "\n");
}
bw.flush();
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
}
}
}

浙公网安备 33010602011771号