Java异常处理 (try catch finally throw throws exception error)

时间:2022-12-15 16:34:17

原文地址:http://chenxiaoqiong.com/articles/tryCatch/

异常分类

  Java异常处理 (try catch finally throw throws exception error)
  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()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常

注意事项

  1. 子类抛出的异常必须是父类抛出异常的一个子集,不能抛出新异常。
  2. 不要让try块过于庞大
    • 阅读代码的时候,在try块冗长的代码中,不容易知道到底是哪些代码会抛出哪些异常,不利于代码维护。
    • 使用try捕获异常是以程序执行效率为代价的,将不需要捕获异常的代码包含在try块中,影响了代码执行的效率。
  3. 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)
{
……
}