最近遇到有关ERROR的处理问题,下面这篇文章
转至:http://www.cnblogs.com/deepnighttwo/archive/2006/12/11/1964305.html
LinkageError是一个比较棘手的异常,准确的说它是一个Error而不是Exception。java api对它没有直接的解释,而是介绍了它的子类:
Subclasses of LinkageError indicate that a class has some dependency on another class; however, the latter class has incompatibly changed after the compilation of the former class.
public class Test24 {
public static void main(String[] args){
foo1();
}
public static void foo1(){
try{
Test01 t1 = new Test01();
// throw new RuntimeException();
}catch(Exception e){
System.out.println("111111111");
//e.printStackTrace();
}finally{
System.out.println("222222222");
}
System.out.println("333333333");
int i=0;
i++;
System.out.println(i);
}
}
所以虽然可以捕获这些错误,用 catch(Throwable e) 的方式,但还是不建议这么做,业务代码还是 catch (Exception e) ,
8.3 Java 的异常处理
异常的处理主要包括捕获异常、程序流程的跳转和异常处理语句块的定义等。当一个异常被抛出时,应该有专门的语句来捕获这个被抛出的异常对象,这个过程被称为捕获异常。当一个异常类的对象被捕获后,用户程序就会发生流程的跳转,系统中止当前的流程而跳转至专门的异常处理语句块,或直接跳出当前程序和 Java 虚拟机,退回到操作系统。
8.3.1 异常类说明
Java 中所有的异常都由类来表示。所有的异常都是从一个名为 Throwable 的类派生出来的。因此,当程序中发生一个异常时,就会生成一个异常类的某种类型的对象。 Throwable 类有两个直接子类: Exception 和 Error 。
与 Error 类型的异常相关的错误发生在 Java 虚拟机中,而不是在程序中。错误类( Error )定义了被认为是不能恢复的严重错误条件。在大多数情况下,当遇到这样的错误时,建议让该程序中断。这样的异常超出了程序可控制的范围。
由程序运行所导致的错误由 Exception 类来表示,该异常类定义了程序中可能遇到的轻微的错误条件。可以编写代码来处理这样的异常并继续执行程序,而不是让程序中断。它代表轻微的可以恢复的故障。接收到异常信号后,调用方法捕获抛出的异常,在可能的情况下再恢复回来,这样程序员可以通过处理程序来处理异常。
Java 中的异常类具有层次组织,其中 Throwable 类是 Error 类(错误类)和 Exception 类(异常类)的父类, Throwable 类是 Object 类的直接子类。
异常类( java.lang.Exception )继承于 java.lang.Object 类中的 java.lang.Throwable 类。异常可分为执行异常( Runtime Exception )和检查异常( Checked Exception )两种,如图 8-1 所示。为了深入了解执行异常和检查异常内容,这里给出它们的详细介绍列举。
图 8-1 异常类的继承结构
1.执行异常
执行异常即运行时异常,继承于 Runtime Exception 。 Java 编译器允许程序不对它们做出处理。下面列出了主要的运行时异常。
· ArithmeticException : 一个非法算术运算产生的异常。
· ArrayStoreException : 存入数组的内容数据类型不一致所产生的异常。
· ArrayIndexOutOfBoundsException : 数组索引超出范围所产生的异常。
· ClassCastException : 类对象强迫转换造成不当类对象所产生的异常。
· IllegalArgumentException : 程序调用时,返回错误自变量的数据类型。
· IllegalThreadStateException : 线程在不合理状态下运行所产生的异常。
· NumberFormatException : 字符串转换为数值所产生的异常。
· IllegalMonitorStateException : 线程等候或通知对象时所产生的异常。
· IndexOutOfBoundsException : 索引超出范围所产生的异常。
· NegativeException : 数组建立负值索引所产生的异常。
· NullPointerException : 对象引用参考值为 null所产生的异常。
· SecurityException : 违反安全所产生的异常。
2.检查异常
除了执行异常外,其余的子类是属于检查异常类也称为非运行时异常,它们都在 java.lang 类库内定义。 Java 编译器要求程序必须捕获或者声明抛弃这种异常。下面列出了主要的检查异常。
· ClassNotFoundException : 找不到类或接口所产生的异常。
· CloneNotSupportedException : 使用对象的 clone( )方法但无法执行 Cloneable所产生的异常。
· IllegalAccessException : 类定义不明确所产生的异常。
· InstantiationException : 使用 newInstance( )方法试图建立一个类 instance时所产生的异常。
· InterruptedException : 目前线程等待执行,另一线程中断目前线程所产生的异常。
8.3.2 错误分类
Error 类与异常一样,它们都是继承自 java.lang.Throwable 类。 Error 类对象由 Java 虚拟机生成并抛出。 Error 类包括 LinkageError (结合错误)与 VitualmanchineError (虚拟机错误)两种子类。
1. LinkageError
LinkageError 类的子类表示一个类信赖于另一个类,但是,在前一个类编译之后,后一个类的改变会与它不兼容。
LinkageError 类包括 ClassFormatError 、 ClassCircularityError 、 ExceptionInitializerError 、 NoClassDeFormatError 、 VeritfyError 、 UnsatisfidLinkError 和 IncompatibleClassChangeError 等子类。其中 NoIncompatibleClassChangeError 类又包含 AbstractMethodError 、 NoSuchField Error 、 NoSuchMethodError 、 IllegalAccessError 和 InstantiationError 子类。这些类所代表的意义如下所述。
· ClassFormatError : 类格式所产生的错误。
· ClassCircularityError : 无限循环所产生的错误。
· ExceptionInitializerError : 初始化所产生的错误。
· NoClassDeFormatError : 没有类定义所产生的错误。
· VeritfyError : 类文件某些数据不一致或安全问题所产生的错误。
· UnsatisfidLinkError : Java虚拟机无法找到合适的原始语言( Native-Language)定义的方法所产生的错误。
· IncompatibleClassChangeError : 不兼容类所产生的错误。
· AbtractMethodError : 调用抽象方法所产生的错误。
· NoSuchFieldError : 存取或改变数据域所产生的错误。
· NoSuchMethodError : 调用类方法所产生的错误。
· IllegalAccessError : 不合法存取或改变数据域调用方法所产生的错误。
· InstantiationError : 使用抽象类或接口所产生的错误。
2. VitualmachineError
当 Java 虚拟机崩溃了或用尽了它继续操作所需的资源时,抛出该错误。
VitualmachineError 包含 InternalError 、 OutOfMemoryError 、 StackOverflow Error 和 UnknownError 。这些类所代表的意义如下所述。
· InternalError : 虚拟机内部所产生的错误。
· OutOfMemoryError : 虚拟机内存不足所产生的错误。
· StackOverflowError : 堆栈无法容纳所产生的错误。
· UnknownError : 虚拟机不知名异常所产生的错误。
8.3.3 异常处理机制
Java 提供了一种独特的异常处理机制,通常通过异常来处理程序设计中可能出现的错误 。
在 Java 程序的执行过程中,如果出现了异常事件,就会生成一个异常对象;生成的异常对象将传递给 Java 运行系统,这一异常的产生和提交过程称为抛弃( Throw )异常。当 Java 运行系统得到一个异常对象时,它将会寻找处理这一异常的代码,找到能够处理这种类型异常的方法后,运行系统把当前异常对象交给这个方法进行处理,这一过程称为捕获( Catch )异常。如果 Java 运行系统找不到可以捕获异常的方法,则运行系统将终止,相应的 Java 程序也将退出。
Java 异常处理是通过 5 个关键字来管理的。它们是 try 、 catch 、 throw 、 throws 和 finally, 将在下面的小节中详细介绍。这里先大致给出它们的工作原理。
程序中需要被监测的程序语句序列应包含在一个 try 代码块中。如果 try 代码块中有异常发生,那么就要抛出该异常。可以用 catch 代码块来捕获这个异常,并且在 catch 代码块中加以适当的处理。系统产生的异常会由 Java 运行系统自动抛出。如果需要手动抛出异常,则使用关键字 throw 。在某些情况下,从一个方法抛出的异常必须用一个 throw 语句指定为异常。最后,从 try 代码块退出时,必须执行的代码要放在一个 finallly 代码块中。
异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。传统的处理异常的办法是:方法返回一个特殊的结果来表示出现异常,调用该方法的程序负责检查并分析函数返回的结果并进行相应的处理。但是,这样做有如下弊端:
· 函数返回 - 1代表出现异常,但是如果函数确实要返回 - 1这个正确的值时,就会出现混淆。
· 可读性降低,将程序代码与处理异常的代码交叉在一起。
· 由调用函数的程序来分析错误,这就要求客户程序员对库函数有很深的了解。
J ava 的异常可以分为运行时异常和非运行时异常两类。继承于 RuntimeException 的类都属于运行时异常,例如算术异常、数组下标越界异常等。由于这些异常产生的位置是未知的, Java 编译器允许程序员在程序中不对它们做出处理。除了运行时异常之外的其他由 Exception 继承来的异常都是非运行时异常, Java 编译器要求在程序中必须处理这种异常 。
8.3.4 异常处理语句
异常处理的目的并不是为了避免发生异常,而是在异常发生时避免程序的异常终止,设法将损失降低到最小。
Java 的异常处理是通过 5 个关键词来实现的: try 、 catch 、 throw 、 throws 和 finally 。一般情况下是用 try 来执行一段程序,如果出现异常,系统会抛出( throw )一个异常,这时候可以通过它的类型来捕捉( catch )它,最后( finally )由默认处理器来处理。
1. try/catch语句块
在 Java 程序里,异常对象是依靠 try/catch 语句来捕获和处理的。 try/catch 异常处理语句分为 try 语句块和 catch 语句块,其格式如下。
try{
…… //try语句块,可能产生异常的多个语句
}catch{
…… //catch语句块,对异常进行处理
}
一般将可能产生异常情况语句放在 try 语句块中,这个 try 语句块用来启动 Java 的异常处理机制。凡是可能抛出异常的语句,包括 throw 语句和可能抛出异常的方法的调用语句,都应该包含在这个 try 语句块中。然后在 catch 语句块对异常进行处理。 Java 语言还规定,每个 catch 语句块都应该与一个 try 语句块相对应。
【例 8-1 】 捕获除数为零的异常,并显示相应信息。
class ArithmeticExceptionDemo{
public static void main(String args[]) {
int zero,aInt;
try {// 监视可能产生异常的代码块
zero=0;
aInt=68/zero;
System.out.println("本字符串将不显示。 ");
}catch (ArithmeticException e) { //捕获 divide-by-zero错误
System.out.println("产生用零除错误。 ");
}
System.out.println("在捕获语句后执行的一个语句。 ");
}
}
该程序的执行结果如图 8-2 所示。
图 8-2 捕获 divide-by-zero错误的执行结果
Try 语句块中调用了可能抛出 ArithmeticException 的对象, catch 语句块则专门用来捕获这类异常。可见, catch 语句块应紧跟在 try 语句块的后面。当 try 语句块中的某条语句在执行时产生了一个异常,此时被启动的异常处理机制会自动捕获到它,然后流程自动跳过异常引发点后面的所有尚未执行语句,而转至 try 语句块后面的 catch 语句块,执行 catch 语句块中的语句。从执行的结果还可以看出,异常被捕获并执行完 catch 语句块中的语句后,继续执行 catch 语句块之后的语句。如果没有异常发生,则跳过 catch 语句块。
2. finally语句块
finally 语句块用来控制从 try-catch 语句转移到另一部分之前的一些必要的善后工作,这些工作包括关闭文件或释放其他有关系统资源等。 finally 语句块中的语句是一种强制的、无条件执行的语句,即无论在程序中是否出现异常,无论出现哪一种异常,也不管 try 代码块中是否包含有 break 、 continue 、 return 或者 throw 语句,都必须执行 finally 语句块中所包含的语句。
finally 语句块紧接着 try-catch 结构中的最后一个 catch 语句块,其形式如下:
try{
……
}catch(Exception1 e1){
……
}catch(Exception2 e2){
……
} finally{
……
}
在出现和未出现异常的情况下都要执行的代码可以放到 finally 语句块中。加入了 finally 语句块后有以下 3 种执行情况。
· 没有抛出异常情况: 执行 try语句块后,再执行 finally语句块。
· 代码抛出在 catch 语句块中捕获的一个异常情况: 这时, Java执行 try语句块中直到这个异常情况被抛出为止的所有代码,跳过 try语句块中剩余的代码;然后执行匹配的 catch语句块中的代码和 finally语句块中的代码。
· 代码抛出了一个在 catch 语句块中没有捕获到的异常情况: 这时, Java执行 try语句块中直到这个异常情况被抛出为止的所有代码,跳过 try语句块中剩余的代码;然后执行 finally语句块中的代码,并把这个异常情况抛回到这个方法的调用者。
【例 8-2 】 带有 finally 语句块的程序示例。
class Finally_Demo{
public static void main(String args[]) {
try{
int x=0;
int y=20;
int z=y/x;
System.out.println("y/x 的值是 :"+z);
}catch(ArithmeticException e){
System.out.println(" 捕获到算术异常 : "+e);
}finally{
System.out.println(" 执行到 finally 块内 ! ");
try{
String name=null;
if(name.equals(" 王老三 ")){
// 字符串比较 , 判断 name 是否为 " 王老三 "
System.out.println(" 你 的名字叫王老三。 ");
}
}catch(Exception e){
System.out.println(" 又捕获到另一个异常 : "+e);
}finally{
System.out.println(" 执行到内层的 finally 块内 ! ");
}
}
}
}
该程序的执行结果如图 8-3 所示。
图 8-3 带有 finally语句块的程序的执行结果
在 Java 语言中, try-catch-finally 语句允许嵌套。本例就是将内层的一个 try 嵌套在外层的 finally 语句块内。在程序执行到外层的 try 语句块时,由于分母为零而产生了算术异常,所以程序转移到第一个 catch 语句块。该 catch 语句块捕获了这个算术异常,并进行了处理,之后再转向必须执行的外层的 finally 语句块。因为在该 finally 语句块内又产生了空指针异常(一个 null 字符串和字符串"王老三"进行比较),所以内层 catch 语句块又捕获到 NullPointerException ,最后程序转移到内层的 finally 语句块。
finally 语句块还可以和 break 、 continue 以及 return 等流程控制语句一起使用。当 try 语句块中出现了上述语句时,程序必须先执行 finally 语句块,才能最终离开 try 语句块。
【例 8-3 】 同时有 break 语句和 finally 语句块的程序的执行情况。
class FinallyWithBreakDemo{
public static void main(String args[]) {
for( ; ; )
try{
System.out.println("即将被 break中断,要退出循环了 !");
break;
}finally{
System.out.println("但是 finally块总要被执行到! ");
}
}
}
该程序的执行结果如图 8-4 所示。
图 8-4 同时有 finally语句块和 break语句的程序的执行结果
8.3.5 声明异常
在某些情况下,如果一个方法产生自己不处理或无法处理的异常,它就必须在 throws 子句中声明该异常。也就是说,在 Java 语言中如果在一个方法中生成了一个异常,但是这一方法并不确切地知道该如何对这一异常事件进行处理,这时,这个方法就应该声明抛弃异常,使得异常对象可以从调用栈向后传播,直到有适合的方法捕获它为止。
throws 关键字是在方法声明中用来列出从方法中发出的非起源于 Error 或 RutimeException 中的任何异常。能够主动引发异常的方法必须用 throws 来声明。通常使用 Java 预定义的异常类就可以满足程序开发者的编程需要。
声明抛弃异常是在一个方法中的 throws 子句中指明的。
下面是包含 throws 子句的方法的基本形式。
[修饰符 ] 返回类型 方法名(参数 1,参数 2,……) throws异常列表
{…… }
例如:
public int read ( ) throws IOException
{…… }
throws 子句中同时可以指明多个异常,说明该方法将不对这些异常进行处理,而是声明抛弃它们。例如:
public static void main (String args[ ]) throws IOException, IndexOutOf-
BoudsException
{…… }
【例 8-4 】 声明抛出异常的程序格式。
import java.io.*;
public class ExceptionExam5
{
public static void go() throws IOException
{//方法代码 }
public static void main(String [] args)
{//程序入口主方法代码 }
}
因为考虑到 go() 方法可能产生一个 IOException ,而此时无法处理异常,所以要从 go() 方法抛出这个异常,并且需要用 throws 子句指定异常。另外, Java 的 I/O 系统包含在 java.io 包中,因此 IOException 也包含在其中,所以使用语句“ import java.io.*; ”导入 java.io 包,然后可以直接引用 IOException 。
8.3.6 抛出异常
Java 应用程序在运行时如果出现了一个可识别的错误,就会产生一个与该错误相对应的异常类的对象。这个对象包含了异常的类型和错误出现时程序所处的状态信息,该异常对象首先被交给 Java 虚拟机,由虚拟机来寻找具体的异常处理者。在 Java 中把产生异常对象并将其交给 Java 虚拟机的过程称为抛出异常。
异常类不同,抛出异常的方法也不同,可以分为以下两种。
· 系统自动抛出的异常。
· 语句抛出的异常。
系统定义的所有运行异常都可以由系统自动抛出。例如,以非法的算术操作引发的算术异常,这时系统抛出已定义好的异常类 ArithmeticException 的对象。前面列出的例子中,基本都属于系统自动抛出的异常。
语句抛出的异常是借助 throw 语句定义何种情况产生这种异常。用户程序自定义的异常不可能依靠系统自动抛出,必须使用 throw 语句抛出这个异常类的新对象。系统定义的运行异常也可以由 throw 语句抛出。用 throw 语句抛出异常对象的一般步骤如下:
( 1 )指定或定义一个合适的异常情况类。
( 2 )产生这个类的一个对象。
( 3 )抛出它。
例如:
EOFException e = new EOFException( );
throw e;
使用 throw 语句抛出异常有两种方式:直接抛出和间接抛出。
1.直接抛出异常
直接抛出方式是直接利用 throw 语句将异常抛出,格式为:
throw newExceptionObject;
利用 throw 语句抛出一个异常后,程序执行流程将直接寻找一个捕获( catch )语句,并进行匹配执行相应的异常处理程序,其后的所有语句都将被忽略。
【例 8-5 】 设计自己的异常类,从键盘输入一个 double 类型的数,若不小于 0.0 ,则输出它的平方根;若小于 0.0 ,则输出提示信息“输入错误”。
import java.io.*;
class MyException extends Exception{
void test(double x) throws MyException{
if(x<0.0) throw new MyException(); //条件成立时,执行 throw语句
else System.out.println(Math.sqrt(x));
}
public static void main(String args[]) throws IOException{
MyException n = new MyException();
try{
System.out.print("求输入实数的平方根。请输入一个实数: ");
BufferedReader br=
new BufferedReader(new InputStreamReader(System.in) );
String s=br.readLine();
n.test(Double.parseDouble(s));
}catch(MyException e){
System.out.println("输入错误! ");
}
}
}
程序的两次运行结果如图 8-5 所示。
图 8-5 MyException类的两次运行结果
在这个程序中,定义的异常类通过 extends 子句继承了 Exception 异常类。在 test( ) 方法中,用 throw 语句指定了可能抛出的异常,该语句在参数小于 0 时被执行,产生并抛出异常 。
值得注意的是:在一个方法定义中如果采用了 throw 语句直接抛出异常,则该方法在发生异常的情况下可能没有返回值。本例就属于这种情况。
从本例也可以看出:由于系统不能识别用户自定义的异常,所以需要编程人员在程序中的合适位置创建自定义异常的对象,并利用 throw 语句将这个新异常对象抛出。
2.间接抛出异常
在 Java 程序中,可以在方法的定义中利用 throws 关键字声明异常类型而间接抛出异常。也就是说,当 Java 程序中方法本身对其中出现的异常并不关心或不方便处理时,可以不在方法实现中直接捕获有关异常并进行处理,而是在方法定义的时候通过 throws 关键字,将异常抛给上层调用处理。其形式如下。
public void myMethod1() throws IndexOutOfBoundsException {
……
}
或
public void myMethod2() throws myException1, myException2 {
……
}
在上层调用该方法时,必须捕获有关异常,否则编译时将会出错。例如,调用方法 myMethod2() 时,必须按如下方式进行。
try{
myMethod2
}catch (MyExceptionl e1){
……
}catch(MyException2 e2){
……
}
【例 8-6 】 带有间接抛出异常的类。
public class OutOfRangeException extends Exception{
public OutOfRangeException(){};
public OutOfRangeException(Sting s){
super(s);
}
} // 定义一个异常类
import OutOfRangeException; //装载异常类
import java.io.*;
public class CreatingExceptions{
private static BufferedReader in = new BufferedReader
(new InputStreamReader(System.in));
public static void main (String[] args) throws OutOfRangeException{
final int MIN = 25, MAX = 40;
int value;
OutOfRangeException problem =
new OutOfRangeException ("Input value is out of range.");
//创建一个异常对象并可能抛出它
System.out.print ("Enter an integer value between " + MIN +
" and " + MAX + ", inclusive: ");
try{
value = Integer.parseInt (in.readLine());
}catch (Exception exception) {
System.out.println ("Error reading int data, MIN_VALUE value
returned.");
value = Integer.MIN_VALUE;
}
//确定该异常是否抛出
if (value < MIN || value > MAX)
throw problem;
System.out.println ("End of main method.");
//may never reach this place
}
}
这个例子有两个特征,一是它利用了自定义的异常类 OutOfRangeException ,一旦程序执行违背了所定义的逻辑就抛出这个异常;二是在抛出异常的方法中,利用 throws 关键字声明了 OutOfRangeException 异常类的间接抛出,这样若是在其他地方使用到这个类的对象,也可以捕获这个异常。
使用 throws 子句抛出异常时应注意如下两个问题。
· 一般这种抛出异常的语句应该被定义为在满足一定条件时执行,例如把 throws子句放在 if语句的条件分支中,只有当条件得到满足,即用户定义的逻辑错误发生时才抛出。例如,例 8-6中的条件( value < MIN || value > MAX)满足时,抛出异常。
· 对于含有 throws子句的方法,应该在方法头定义中增加如下部分。
throws异常类名列表
这样做主要是为了通知所有欲调用此方法的方法:由于该方法包含 throws了句,所以要准备接受和处理它在运行过程中可能会抛出的异常。如果方法中的 throws了句不止一个,方法头的异常类名表中的列出的异常也不止一个,应该包含所有可能产生的异常。例如,在上面的 myMethod2( ) 方法中包含的异常有: myException1 和 myException2 。
注意: 执行 throws子语句将中断程序的执行,也就是说 throws的下一条语句将暂停执行。
8.3.7 自定义异常类
在实际的编程中并不一定非要使用 Java 已经定义的异常,经常需要创建自己的异常,以便指出编写的代码可能生成的一个特殊错误。创建自己的异常类,必须从一个现有的异常类型(最终继承自 Throwable 类)继承。继承一个异常同继承一个普通的类的方法是一样的 。
Java 提供的一些异常有时候不能满足编程的需求,例如规定用户输入数据的范围在 20 到 30 之间,但是 Java 并没有这个方面的异常,这时就可以应用自定义的异常来规范化客户的数据输入。
在 Java 中进行自定义异常时,自定义异常类必须是 Throwable 类的直接或间接子类。下面的例子是关于自定义异常的。它通过继承 Exception 类而继承 Throwable 类,即间接继承 Throwable 类。
【例 8-7 】 自定义异常类程序示例。
class OutBoundsException extends Exception
{
OutBoundsException (String mes)
{
// 调用超类的构造函数
super(mes);
}
}
class check
{
String ChecktheNum(int n) throws OutBoundsException
{
Integer N=new Integer(n);
if(n>30||n<20)
throw new OutBoundsException("the number is out of bound!!");
else
return "the number"+N.toString()+"is in the bound!!";
}
}
class Test
{
public static void main(String []args)
{
try
{
check c=new check();
System.out.println(" 以下是合法的数据的报告! ");
System.out.println(c.ChecktheNum(25));
System.out.println(" 以下是非法的数据的报告! ");
System.out.println(c.ChecktheNum(5));
}
catch(OutBoundsException e)
{
System.out.println(e.toString());
}
}
}
运行结果如图 8-6 所示。
图 8-6 运行结果
注意: 一个方法所声明抛弃的异常是作为这个方法与外界交互的一部分而存在的。 所以,方法的调用者必须了解这些异常,并确定如何正确地处理它们。
<script></script>