在Oracle公司编写的《Java使用指南》中关于"异常"的描述占据了相当大的篇幅,可以说对于"异常"的处理是开发人员必须熟练掌握的技能。毕竟,程序员大部分工作不是在改BUG,就是在改BUG的路上。
异常是程序中的一些错误,错误可能来源于程序本身错误,也可能是用户误操作所致,还可能是物理因素引起的。由但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出java.lang.ArithmeticException的异常。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。
如图所示:
我们通常所说的"异常类"指的是Exception,它继承自Throwable;Error也继承自Throwable,但是Error通常不属于程序处理的范畴,它表示不希望被程序捕获或者是程序无法处理的错误。
Exception又可以分为非可查性异常(Unchecked Exception)和可查性异常(Checked Exception),它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。
下面详细讲述下Exception和Error之间的区别和联系:
Java的异常处理本质上是抛出异常和捕获异常。
Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。
使用 try 和 catch 关键字可以捕获异常,代码块放在异常可能发生的地方。
匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。
try{
// 程序代码:用于监听异常,当try语句块内发生异常时,异常就被抛出;
}catch(Exception e){
// catch块:用于捕获异常,捕获try语句块中发生的异常;
}
这里讲一下“多重捕获”的情况:
很多情况下,由单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被旁路。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}catch(Exception e){
// 程序代码
}
如图,通常做法是:Exception >异常类型3>异常类型2>异常类型1
对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。通常,在最后写Exception,来确保异常肯定会被捕获。
try-catch体还可以定义一个finally模块,finally模块通常用于资源的回收。
finally体的特点是不论程序有无异常产生都能得到执行,所以我们可以把try块里资源的回收等操作交给它处理(如数据库连接、网络连接和磁盘文件),以便保证其内容一定能得到执行,看下面的实例:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
注意:只有finally块执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明,意为抛出异常给父集。但有一点要明白:必须有一级代码需要对异常进行处理,不能一直抛出异常,最终交给JVM处理,因为JVM对检查性异常的处理只有一个方法 – 让系统挂掉。
import java.io.*;
public class className{
public void deposit(double amount) throws RemoteException{
// Method implementation
throw new RemoteException();
}
//Remainder of class definition
}
再说说throw/throws的关系:
当异常捕获完成以后,需要在控制台告知程序员程序到底怎么了。下面的列表是 Throwable 类的主要方法:
方法名 | 方法介绍 |
---|---|
public String getMessage() | 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
public Throwable getCause() | 返回一个Throwable 对象代表异常原因。 |
public String toString() | 使用getMessage()的结果返回类的串级名字。 |
public void printStackTrace() | 打印toString()结果和栈层次到System.err,即错误输出流。 |
public StackTraceElement [] getStackTrace() | 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
public Throwable fillInStackTrace() | 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
通常,小编习惯用两种方法来查看异常情况,分别是:e.getMessage() 和 e.printStackTrace() 。
除了系统定义的各种"异常"之外,有时候根据程序的需要,开发人员需要定义自己的"异常",也就是"自定义异常"。"自定义异常"有三个原则:
通常来说,自定义异常继承这两个类即可,原则如下:
我们以继承 Exception 为例:
/* 文件名 : MyException .java */
public class MyException extends Exception {
public MyException() {
super("我是自定义异常");
}
}
/* 文件名 : TestMyException .java */
public class TestMyException {
public void test() throws MyException {
throw new MyException();
}
public static void main(String[] args){
try{
TestMyException test = new TestMyException();
test.test();
}catch(MyException e){
System.out.println(e)
}
}
}
如果自定义异常继承了 RuntimeException ,处理起来会更简单,但是缺乏了 Exception 类的严谨:
最后,我列举一些常见的内置异常,供大家参考学习:
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
更多精彩,请关注我的"今日头条号":Java云笔记
随时随地,让你拥有最新,最便捷的掌上云服务