原文地址:http://chenxiaoqiong.com/articles/tryCatch/
异常分类
Throwable是所有异常的基类,程序中一般不会直接抛出Throwable对象。Exception和Error是Throwable的子类,Exception下面又有RuntimeException和一般的Exception两类。可以把JAVA异常分为三类:
检查性异常(非RuntimeException):所有继承自Exception并且不是RuntimeException的异常都是checked Exception。JAVA 语言规定必须对此类异常作处理,编译器会对此作检查,要么在方法体中声明抛出,要么使用catch语句捕获,不然不能通过编译。
运行时异常(RuntimeException):与检查性异常相反, 编译器不会检查程序是否对RuntimeException作了处理。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去简单的捕获或者抛出。
错误(Error): Error表示程序在运行期间出现了十分严重、不可恢复的错误,在这种情况下应用程序只能中止运行,例如JAVA 虚拟机出现错误。编译器不会检查Error是否被处理,在程序中不用捕获Error类型的异常;一般情况下,在程序中也不应该抛出Error类型的异常。
捕获异常try catch finally
在Java中,异常通过try-catch语句捕获。其一般语法形式为:
try {
// 可能会发生异常的程序代码
} catch (Type1 id1) {
// 捕获并处理try抛出的异常类型Type1
} catch (Type2 id2) {
// 捕获并处理try抛出的异常类型Type2
} finally {
// 无论是否发生异常,都将执行的语句块
}
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
执行过程:当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
抛出异常throw throws
throws关键字放在方法签名的尾部,一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。如果一个方法可能会出现异常,但没有能力处理这种异常,则在方法声明处用throws子句来声明抛出异常。语法格式:
methodName throws Exception1,Exception2,..
{
}
当方法抛出上面的Exception1,Exception2,.. 异常时,方法将不对这些类型及其子类类型的异常作处理,而由调用该方法的调用者去处理。如果调用者不想处理该异常,可以继续向上抛出。如果所有方法都层层上抛获取的异常,最终JVM会进行处理(打印异常消息和堆栈信息)。
throw关键字总是出现在函数体中,用来抛出一个Throwable类型的异常。语法格式:
// 例如抛出一个IOException类的异常对象:
throw new IOException;
throw语句后的语句执行不到。如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。
常用异常方法
下面的列表是 Throwable 类的主要方法:
方法名 | 说明 |
---|---|
public String getMessage() | 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
public Throwable getCause() | 返回一个Throwable 对象代表异常原因。 |
public void printStackTrace() | 打印toString()结果和栈层次到System.err,即错误输出流。 |
public String toString() | 使用getMessage()的结果返回类的串级名字。 |
常见异常
1. runtimeException子类:
异常 | 说明 |
---|---|
java.lang.ArrayIndexOutOfBoundsException | 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。 |
java.lang.ArithmeticException | 算术条件异常。譬如:整数除零等。 |
java.lang.NullPointerException | 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等 |
java.lang.ClassNotFoundException | 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。 |
java.lang.NegativeArraySizeException | 数组长度为负异常 |
java.lang.ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
java.lang.SecurityException | 安全性异常 |
java.lang.IllegalArgumentException | 非法参数异常 |
2.IOException
异常 | 说明 |
---|---|
IOException | 操作输入流和输出流时可能出现的异常。 |
EOFException | 文件已结束异常 |
FileNotFoundException | 文件未找到异常 |
3. 其他
异常 | 说明 |
---|---|
ClassCastException | 类型转换异常类 |
ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
SQLException | 操作数据库异常类 |
NoSuchFieldException | 字段未找到异常 |
NoSuchMethodException | 方法未找到抛出的异常 |
NumberFormatException | 字符串转换为数字抛出的异常 |
StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 |
IllegalAccessException | 不允许访问某类异常 |
InstantiationException | 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常 |
注意事项
- 子类抛出的异常必须是父类抛出异常的一个子集,不能抛出新异常。
- 不要让try块过于庞大
- 阅读代码的时候,在try块冗长的代码中,不容易知道到底是哪些代码会抛出哪些异常,不利于代码维护。
- 使用try捕获异常是以程序执行效率为代价的,将不需要捕获异常的代码包含在try块中,影响了代码执行的效率。
- finally语句块中不要抛出异常,不要使用return
JAVA异常处理机制保证无论在任何情况下必须先执行finally块然后在离开try块,因此在try块中发生异常的时候,JAVA虚拟机先转到finally块执行finally块中的代码,finally块执行完毕后,再向外抛出异常。如果在finally块中抛出异常,try块捕捉的异常就不能抛出,外部捕捉到的异常就是finally块中的异常信息,而try块中发生的真正的异常堆栈信息则丢失了。
例子:
public class TestException {
public TestException() {
}
boolean testEx() throws Exception {
boolean ret = true;
try {
ret = testEx1();
return ret;
} catch (Exception e) {
System.out.println("testEx, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx, finally; return value=" + ret);
}
}
boolean testEx1() throws Exception {
boolean ret = true;
try {
int b = 12;
for (int i = 2; i >= -2; i--) {
int c = b / i;
System.out.println("i=" + i);
}
return true;
} catch (Exception e) {
System.out.println("testEx1, catch exception");
ret = false;
throw e;
} finally {
System.out.println("testEx1, finally; return value=" + ret);
//此处抛出新的异常,将覆盖catch中抛出的异常
//throw new Exception("testEx1, finally; throw new Exception");
//此处return,catch中排出的异常将无法捕获
return ret;
}
}
public static void main(String[] args) {
TestException testException1 = new TestException();
try {
testException1.testEx();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
建议:当你需要一个地方来执行在任何情况下都必须执行的代码时,使用finally块。比如当你的程序中使用了外界资源,如数据库连接,文件等,使用finally块来释放资源。
4. 不要一次捕获所有异常
try
{
method1(); //method1抛出ExceptionA
method2(); //method1抛出ExceptionB
method3(); //method1抛出ExceptionC
}
catch(Exception e)
{
……
}
这里有两个潜在的缺陷
- 分别处理就不能实现:针对try块中抛出的每种Exception,很可能需要不同的处理和恢复措施
- 代码中捕获了所有可能抛出的RuntimeException而没有作任何处理,掩盖了编程的错误,会导致程序难以调试。
正确代码:
try
{
method1(); //method1抛出ExceptionA
method2(); //method1抛出ExceptionB
method3(); //method1抛出ExceptionC
}
catch(ExceptionA e)
{
……
}
catch(ExceptionB e)
{
……
}
catch(ExceptionC e)
{
……
}