本文重在 Java 中异常机制的一些概念 。写本文的目的在于方便我很长时间后若是忘 了这些东西可以通过这 片文章迅速回忆起来。
1. 异常机制
1.1
异常机制是指当程序出现错误后 ,程序如何处理。具体来说,异常机制提供了程序 退出的安全通道。当出现错误后,程序执行的流程发生改变 ,程序的控制权转移到异常处理 器。
1.2
传统的处理异常的办法是 ,函数返回一个特殊的结果来表示出现异常 (通常这个特 殊结果是大家约定俗称的 ),调用该函数的程序负责检查并分析函数返回的结果 。这样做有 如下的弊端:例如函数返回-1 代表出现异常,但是如果函数确实要返回 -1 这个正确的值时 就会出现混淆;可读性降低,将程序代码与处理异常的代码混爹在一起 ;由调用函数的程序 来分析错误,这就要求客户程序员对库函数有很深的了解 。
1.3 异常处理的流程
1.3.1 遇到错误,方法立即结束,并不返回一个值;同时,抛出一个异常对象
1.3.2 调用该方法的程序也不会继续执行下去 ,而是搜索一个可以处理该异常的异常 处理器,并执行其中的代码
2 异常的分类
2.1 异常的分类
2.1.1
异常的继承结构:基类为 Throwable,Error 和 Exception 继承 Throwable, RuntimeException 和 IOException 等继承 Exception ,具体的 RuntimeException 继承 RuntimeException。
2.1.2
Error 和 RuntimeException 及其子类成为未检查异常(unchecked),其它异常 成为已检查异常(checked)。
2.2 每个类型的异常的特点
2.2.1 Error 体系
Error 类体系描述了 Java 运行系统中的内部错误以及资源 耗尽的情形。应用程 序不应该抛出这种类型的对象 (一般是由虚拟机抛出 )。如果出现这种错误 ,除了尽力使程 序安全退出外,在其他方面是无能为力的 。所以,在进行程序设计时,应该更关注 Exception 体系。
2.2.2 Exception 体系
Exception 体系包括 RuntimeException 体系和其他非 RuntimeException 的体系
2.2.2.1 RuntimeException
RuntimeException 体系包括错误的类型转换 、数组越界访问和试图访问空指针等 等。处理 RuntimeException 的原则是:如果出现 RuntimeException,那么一定是程序员的 错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常 。
2.2.2.2 其他(IOException 等等)
这类异常一般是外部错误 ,例如试图从文件尾后读取数据等 ,这并不是程序本身的 错误,而是在应用环境中出现的外部错误 。
2.3 与 C++异常分类的不同
2.3.1
其实,Java 中 RuntimeException 这个类名起的并不恰当,因为任何异常都是运行时出现的 。
(在编译时出现的错误并不是异常 ,换句话说,异常就是为了解决程序运行时出现的的错 误)。
2.3.2
C++中 logic_error 与 Java 中的 RuntimeException 是等价的,而 runtime_error 与 Java 中
非 RuntimeException 类型的异常是等价的。
3 异常的使用方法
3.1 声明方法抛出异常
3.1.1 语法:throws(略)
3.1.2 为什么要声明方法抛出异常 ?
方法是否抛出异常与方法返回值的类型一样重要 。假设方法抛出异常确没有声明该方法将抛 出异常,那么客户程序员可以调用这个方法而且不用编写处理异 常的代码。那么,一旦出现 异常,那么这个异常就没有合适的异常控制器来解决 。
3.1.3 为什么抛出的异常一定是已检查异常 ?
RuntimeException 与 Error 可以在任何代码中产生,它们不需要由程序员显示的抛出 ,一 旦出现错误,那么相应的异常会被自动抛出 。而已检查异常是由程序员抛出的 ,这分为两种 情况:客户程序员调用会抛出异常的库函数 (库函数的异常由库程序员抛出 );客户程序员
自 己 使 用 throw 语 句 抛 出 异 常 。 遇 到 Error , 程 序 员 一 般 是 无 能 为 力 的 ; 遇 到 RuntimeException ,那么一定是程序存在 逻辑错误,要对程序进行修改 (相当于调试的一 种方法);只有已检查异常才是程序员所关心的 ,程序应该且仅应该抛出或处理已检查异常 。
3.1.4
注意:覆盖父类某方法的子类方法不能抛出比父类方法更多的异常 ,所以,有时设计父类的 方法时会声明抛出异常 ,但实际的实现方法的代码却并不抛出异常 ,这样做的目的就是为了 方便子类方法覆盖父类方法时可以抛出异常 。
3.2 如何抛出异常
3.2.1 语法:throw(略)
3.2.2 抛出什么异常?
对于一个异常对象,真正有用的信息时异常的对象类型 ,而异常对象本身毫无意义 。比如一 个异常对象的类型是 ClassCastExceptio n,那么这个类名就是唯一有用的信息 。所以,在 选择抛出什么异常时,最关键的就是选择异常的类名能够明确说明异常情况的类 。
3.2.3
异常对象通常有两种构造函数 :一种是无参数的构造函数 ;另一种是带一个字符串的构造函 数,这个字符串将作为这个异常对象除了类型名以外的额外说明 。
3.2.4
创建自己的异常:当 Java 内置的异常都不能明确的说明异常情况的时候 ,需要创建自己的 异常。需要注意的是,唯一有用的就是类型名这个信息 ,所以不要在异常类的设计上花费精 力。
3.3 捕获异常
如果一个异常没有被处理 ,那么,对于一个非图形界面的程序而言 ,该程序会被中止并输出 异常信息;对于一个图形界面程序 ,也会输出异常的信息,但是程序并不中止,而是返回用
Ы 缑娲 硌分小?BR> 3.3.1 语法:try、catch 和 finally(略)
控制器模块必须紧接在 try 块后面。若掷出一个异常,异常控制机制会搜寻参数与异常类型 相符的第一个控制器随后它会 进入那个 catch
从句,并认为异常已得到控制 。一旦 catch 从句结束对控制器的搜索也会停止 。
3.3.1.1 捕获多个异常(注意语法与捕获的顺序)(略)
3.3.1.2 finally 的用法与异常处理流程(略)
3.3.2 异常处理做什么?
对于 Java 来说,由于有了垃圾收集,所以异常处理并不需要回收内存 。但是依然有一些资 源需要程序员来收集,比如文件、网络连接和图片等资源 。
3.3.3 应该声明方法抛出异常还是在方法中捕获异常 ? 原则:捕捉并处理哪些知道如何处理的异常 ,而传递哪些不知道如何处理的异常
3.3.4 再次抛出异常
3.3.4.1 为什么要再次抛出异常 ?
在本级中,只能处理一部分内容,有些处理需要在更高一级的环境中完成,所以应该再次抛 出异常。这样可以使每级的异常处理器处理它能够处理的异常 。
3.3.4.2 异常处理流程
对应与同一 try 块的 catch 块将被忽略,抛出的异常将进入更高的一级 。
4 关于异常的其他问题
4.1 过度使用异常
首先,使用异常很方便,所以程序员一般不再愿意编写处理错误的代码 ,而仅仅是简简单单 的抛出一个异常。这样做是不对的,对于完全已知的错误,应该编写处理这种错误的代码 , 增加程序的鲁棒性。另外,异常机制的效率很差 。
4.2 将异常与普通错误区分开
对于普通的完全一致的 错误,应该编写处理这种错误的代码 ,增加程序的鲁棒性。只有外部 的不能确定和预知的运行时错误才需要使用异常 。
4.3 异常对象中包含的信息
一般情况下,异常对象唯一有用的信息就是类型信息 。但使用异常带字符串的构造函数时 ,
这 个 字 符 串 还 可 以 作为 额 外 的 信 息 。 调 用异 常 对 象 的 getMessage() 、 toString() 或 者 printStackTrace()方法可以分别得到异常对象的额外信息 、类名和调用堆栈的信息 。并且后 一种包含的信息是前一种的超集 。
程序出了那种异常 JVM 就会抛出相应的异常 比如代码:ArrayIndexOutOfBoundsException public void demo1(){
try{
int a = 10/0;
}catch(ArithmeticException ae){
System.out.println(“ 算术运算异常:”+ae.getMessage());
}catch(Exception e){
System.out.println(“ 其他异常”+e.getMessage());
}
}
public void demo2(){
String strList[] = {"a","b","c"};
try{
String str = strList[4].toString();
}catch(ArrayIndexOutOfBoundsException ae){
System.out.println(“ 数组下标越界:”+ae.getMessage());
}catch(Exception e){
System.out.println(“ 其他异常”+e.getMessage());
}
}
总之 Exception 是所有异常的父类.如果你出现的异常被他的子类捕捉了 ,他就不会再捕捉比
如 demo2()方法如果是出现了 ArrayIndexOutOfBounds Exception
Exception 就不会捕捉了!
那么为什么要捕捉多次呢 ?因为 ArrayIndexOutOfBoundsExcept ion 只是数组下标越界的异 常,所以它比 Exception 更的仔细,更能说明异常的原因!
如果不是出现 ArrayIndexOutOfBoundsEx ception 则 Exception 就会来捕捉
异常的创建是指你自己创建一个异常 ,然后手动的进行抛出 ,所有异常都继承于 Exception
父类,你可以按照这个来写自己的异常
class MyException extends Exception {
//.....你做处理
}
当你在程序中使用的时候 ,一般会这样使用
public static void main(String[] args) {
try {
} catch (MyException me) {// 这里处理你自己的异常情况
} catch (Exception e) {// 这里处理没有自己异常,其他异常的处理
}