java异常处理机制
1:什么是异常?
异常(Exception)也叫例外。在Java编程语言中,异常就是程序在运行过程中由于硬件设备问题、软件设计错误、缺陷等导致的程序错误。在软件开发过程中,很多情况都将导致异常的产生,例如:
(1) 想打开的文件不存在;
(2) 网络连接中断;
(3) 操作数超出预定范围;
(4) 正在装载的类文件丢失;
(5) 访问的数据库打不开;
可见,在程序中产生异常的现象是非常普遍的。在Java编程语言中,对异常的处理有非常完备的机制。异常本身作为一个对象,产生异常就是产生一个异常对象。这个对象可能由应用程序本身产生,也
可能由 Java 虚拟机产生,这取决于产生异常的类型。该异常对象中包括了异常事件的类型以及发生异常时应用程序目前的状态和调用过程。请看下面产生异常的例子
文件操作将产生异常
// Exception1.java
import java.io.*;
class Exception1 {
p lic static void main(String args[]){
FileInputStream fis =new FileInputStream("text.txt");
int b;
while((b=fis.read())!=-1) {
System.out.print(b);
}
fis.close();
}
}
当编译这个程序时,屏幕上会输出下面的信息:
D:\user\chap08>javac Exception1.java
Exception1.java:5: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
FileInputStream fis = new FileInputStream("text.txt");
^
Exception1.java:7: unreported exception java.io.IOException; must be caught or declared to be thrown
while((b=fis.read())!=-1) {
^
Exception1.java:10: unreported exception java.io.IOException; must be caught ordeclared to be thrown
fis.close();
^
3 errors
数组下标超界的例子
//Exception2.java
p lic class Exception2{
p lic static void main (String args[]) {
String langs [] = {"Java","Visaul Basic","C++"};
int i = 0;
while (i < 4) {
System.out.println (langs[i]);
i++;
}
}
}
程序的编译和运行结果如下:
D:\user\chap08>javac Exception2.java
D:\user\chap08>java Exception2
Java
Visaul Basic
C++
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at Exception2.main(Exception2.java:8)
上面的例子编译可以通过,但运行时出现异常信息被抛出。在其循环被
执行四次之后,数组下标溢出,程序终止,并带有错误信息,就象前面
所示的程序那样。
被0除的例子
// Exception3.java
class Exception3{
p lic static void main(String args[]){
int a=0;
System.out.println(5/a);
}
}
我们编译这个程序得到其字节码文件,然后运行它,屏幕上的显示如
下:
D:\user\chap08>javac Exception3.java
D:\user\chap08>java Exception3
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Exception3.main(Exception3.java:5)
因为除数不能为0,所以在程序运行的时候出现了除以0溢出的异常事
件。
在上面的三个例子中,我们都遇到了异常。屏幕上所显示的信息
java.io.IOException、
java.io.FileNotFoundException、
java.lang.ArrayIndexOutOfBoundsException
以及java.lang.ArithmeticException
分别指明了异常的类型以及异常所在的包。同时我们也可以看到,对
于某些异常,在程序中必须对它进行处理,否则编译程序会指出错误
(如例1)。但对另一些异常,在程序中可以不做处理,而直接由运行时
系统来处理(如例3)。在下节中,我们将详细了解这两类异常,以及在
程序中如何处理这两类异常。
2 异常处理机制
(1)Java的异常处理机制:
在 Java 程序的执行过程中,如果出现了异常事件,就会生成一个异常对象。这个对象可能是由正在运行的方法生成,也可能由Java虚拟机生成,其中包含一些信息指明异常事件的类型以及当异常发生
时程序的运行状态等。Java语言提供两种处理异常的机制:
(1)捕获异常
在Java程序运行过程中系统得到一个异常对象时,它将会沿着方法的调用栈逐层回溯,寻找处理这一异常的代码。找到能够处理这种类型异常的方法后,运行时系统把当前异常对象交给这个方法进行处
理,这一过程称为捕获(catch)异常。这是一种积极的异常处理机制。如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。
(2)声明抛弃异常
当Java程序运行时系统得到一个异常对象时,如果一个方法并不知道如何处理所出现的异常,则可在方法声明时,声明抛弃(throws)异常。
(2)异常类的类层次:
前面已经提到,Java 是采用面向对象的方法来处理错误的,一个异常事件
是由一个异常对象来代表的。这些异常对象都对应于类java.lang.Throwable及
其子类。下面我们就来看一下异常类的层次。
在 java类库的每个包中都定义了自己的异常类,所有这些类都直接或间接
地继承于类Throwable。图1列举了一些异常类并指明了它们的继承关系。
Java中的异常事件分为两大类。一类继承于类Error,它的继承关系如下:
java.lang.Object
java.lang.Throwable
java.lang.Error
常见的错误类有AnnotationFormatError, AssertionError, AWTError,
LinkageError, CoderMalfunctionError, FactoryConfigurationError,ThreadDeath,
Virt lMachineError, TransformerFactoryConfigurationError等,包括动态链接
失败、线程死锁、图形界面错误、虚拟机错误等,通常Java程序不应该捕获这
类异常,也不会抛弃这种异常。
另外一类异常则继承于类Exception,这是Java程序中所大量处理的异常。它
的继承关系如下:
java.lang.Object
java.lang.Throwable
java.lang.Exception
常见异常类有:AclNotFoundException, ApplicationException, AWTException,
BackingStoreException, ClassNotFoundException,
CloneNotSupportedException, DataFormatException, DestroyFailedException,
ExecutionException, PrintException, GeneralSecurityException,
InterruptedException, InvalidPreferencesFormatException, ParseException,
RuntimeException, SAXException, SQLException, TimeoutException,
TransformerException, UnsupportedCallbackException,
UnsupportedLookAndFeelException, URISyntaxException, UserException,
XAException, XMLParseException, XPathException 等
其中包括了运行时异常和非运行时异常。
常见的运行时异常如下:
(1) 类型转换异常ClassCastException
String strName=new string(“123”);
int nNumber=(int)strName;
(2) 数组超界异常ArrayIndexOutBoundsException
int[] b=new int[10];
b[10]=1000;
(3) 指定数组维数为负值异常NegativeArraySizeException
b[-1]=1001;
(4) 算术异常ArithmeticException
int b=0;
a=500/b;
(5) Java系统内部异常InternalException
JVM抛出的异常。
(6) 类型不符合异常IncompatibleTypeException
int n=12345;
String s=(String)n;
(7) 内存溢出异常OutOfMemeoryException
(8) 没有找到类定义异常NoClassDefFoundException
aClass aa=new aClas(); //但aClass类未定义。
(9) 空指针异常 NullPointerException
int b[ ];
b[0]=99; //没有实例化,就访问,将产生空指针。
常见的非运行时异常如下:
(1) ClassNotFoundException :找不到类或接口所产生的异常
(2) CloneNotSupportedException:使用对象的 clone 方法但无法执行 Cloneable 所产生的异常。
(3) IllegalAccessException :类定义不明确所产生的异常。例如:类不为 p lic ,或是包含一个类定义在另一个类库内。
(4) IOException :在一般情况下不能完成I/O操作所产生的异常。
(5) EOFException: 打开文件没有数据可以读取所产生的异常
(6) FileNotFoundException :在文件系统中,找不到文件名称或路径所产生
(7) InterruptedIOException:目前线程等待执行,另一线程中断目前线程I/O运行所产生的异常
在Sun公司提供的各种API包中,如java.io,java.net,java.awt等,都提供不同情况下可能产生的异常。由于异常的种类非常多,需要读者实际运用中逐渐掌握。
(3)Throwable类的常用方法:
java.lang.Throwable类是所有Error类和Exception类的父类,常用的方法有 :fillInStackTrace() 、getLocalizedMessage() 、getMessage() 、printStackTrace()、printStackTrace(PrintStream)
、 printStackTrace(PrintWriter) 、toString()。
(1) p lic native Throwable fillInStackTrace()
填写执行堆栈跟踪信息。 该方法在应用程序重新抛出错误或异常时有用。例如:
try {
a = b / c;
} catch(ArithmeticThrowable e) {
a = Number.MAX_VAL;
throw e.fillInStackTrace();
}
(2) p lic String getLocalizedMessage()
生成该 Throwable 的本地化描述。 子类可能会覆盖该方法以便产生一个特定于本地的消息。 对于未覆盖该方法的子类,缺省地返回调用 getMessage() 的结果。
(3) p lic String getMessage()
返回该 throwable 对象的详细信息。如果该对象没有详细信息则返回null。
(4) p lic void printStackTrace()
把该 Throwable 和它的跟踪情况打印到标准错误流。
(5) p lic void printStackTrace(PrintStream s)
把该 Throwable 和它的跟踪情况打印到指定打印流。
(6) p lic void printStackTrace(PrintWriter s)
把该 Throwable 和它的跟踪情况打印到指定打印流。
(7) p lic String toString()
返回该 throwable 对象的简短字符串描述。
3 异常的处理
(1)捕获异常try-catch-finally:
一个方法中如果对某种类型的异常对象提供了相应的处理代码,则这个方法可捕获该种异常。捕获异常是通过try-catch-finally语句实现的。其语法为:
try{ ...... }catch( ExceptionName1 e ){ ...... }catch( ExceptionName2 e ){ ...... } ...... }finally{ ...... }
1、try
捕获异常的第一步是用try{…}选定捕获异常的范围,由try所限定的代码块中的语句在执行过程中可能会生成异常对象并抛弃。
2、catch
每个try代码块可以伴随一个或多个catch语句,用于处理try代码块中所生成的异常事件。catch语句只需要一个形式参数来指明它所能够捕获的异常类型,这个类必须是Throwable的子类,运行时系统通过
参数值把被抛弃的异常对象传递给catch块。
catch块中的代码用来对异常对象进行处理,与访问其它对象一样,可以访问一个异常对象的变量或调用它的方法。getMessage( )是类Throwable所提供的方法,用来得到有关异常事件的信息,类
Throwable还提供了方法printStackTrace( )用来跟踪异常事件发生时执行堆栈的内容。例如:
try{ ...... }
catch( FileNotFoundException e )
{
System.out.println( e );
System.out.println( "message: "+e.getMessage()
);
e.printStackTrace( System.out );
}catch( IOException e )
{
System.out.println( e );
}
3、catch 语句的顺序 捕获异常的顺序和catch语句的顺序有关,当捕获到一个异常时,剩下的catch语句就不再进行匹配。因此,在安排catch语句的顺序时,首先应该捕获最特殊的异常,然后再逐渐
一般化。也就是一般先安排子类,再安排父类。例如上面的程序如果安排成如下的形式:
try{ ...... }
catch(IOException e )
{
System.out.println( e );
System.out.println( "message: "+e.getMessage() );
e.printStackTrace( System.out );
}catch(FileNotFoundException e )
{
System.out.println( e );
}
由于第一个catch语句首先得到匹配,第二个catch语句将不会被执行。编译时将出现“catch not reached”的错误。
4、finally
捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。一般是用来关闭文件或释放其他的系统资源。虽然
finally作为try-catch-finally结构的一部分,但在程序是可选的,也就是说可以没有 finally语句。如果存在finally语句,不论try块中是否发生了异常,是否执行过catch 语句,都要执行finally语
句。
另外,try-catch-finally可以嵌套。
(2)声明抛出异常:
如果在一个方法中生成了一个异常,但是这一方法并不确切地知道该如何对这一异常事件进行处理,这时,该方法就应该声明抛弃异常,使得异常对象可以从调用栈向后传播,直到有合适的方法捕获它
为止。 声明抛弃异常是在一个方法声明中的throws子句中指明的。例如: p lic int read () throws IOException{ ……
}
throws子句中同时可以指明多个异常,之间由逗号隔开。例如:
p lic static void main(String args[]) throws
IOException,IndexOutOfBoundsException {
……
}
最后,我们再次强调,对于非运行时例外,如前例中的IOException等,程序中必须要作出处理,或者捕获,或者声明抛弃。而对于运行时例外,如前例中的ArithmeticException,
IndexOutOfBoundsException,则可以不做处理
抛弃异常的例子(对之前的进行改进)
// Exception4.java
import java.io.*;
p lic class Exception4{
p lic static void main(String args[])throws FileNotFoundException,IOException{
FileInputStream fis=new FileInputStream(“text.txt”);
int b;
while((b=fis.read())!=-1){
System.out.print(b);
}
fis.close();
}
}
捕获异常的例子(对之前的进行改进)
import java.io.*;
p lic class Exception5{
p lic static void main(String args[]) {
try{
FileInputStream fis=new FileInputStream(“text.txt”);
int b;
while((b=fis.read())!=-1){
System.out.print(b);
}
fis.close();
}catch(FileNotFoundException e)
{
System.out.println( e );
System.out.println( "message: "+e.getMessage() ); e.printStackTrace( System.out );
}catch(IOException e)
{
System.out.println( e );
}
}
(3)抛出异常:
抛出异常就是产生异常对象的过程,首先要生成异常对象,异常或者由虚拟机生成,或者由某些类的实例生成,也可以在程序中生成。在方法中,抛出异常对象是通过throw语句实现的。
例如:
IOException e=new IOException();
throw e ;
可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
throw new String("throw anything");
自定义异常类必须是Throwable的直接或间接子类。
显示抛出异常详细情况的例子
//Exception6.java
p lic class Exception6 {
p lic static void main(String[] args) {
try {
throw new Exception("My Exception"); } catch (Exception e)
{
System.err.println("Caught Exception");
System.err.println("getMessage():" + e.getMessage());
System.err.println("getLocalizedMessage():"+ e.getLocalizedMessage());
System.err.println("toString():" + e);
System.err.println("printStackTrace():"); e.printStackTrace();
}
}
}
程序运行结果如下:
d:\user\chap08>java Exception6
Caught Exception
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exception
at Exception6.main(Exception6.java:5)
try-catch-finally嵌套的例子
class MyoneException extends Exception {
}
p lic class TryInbed {
p lic static void main(String[] args) {
System.out.println("Entering first try block");
try {
System.out.println("Entering second try block");
try {
throw new MyoneException();
} finally {
System.out.println("finally in 2nd try block");
}//try-catch-finally嵌套在try限定的范围内。
} catch (MyoneException e) {
System.err.println("Caught MyoneException in 1st try block");
} finally {
System.err.println("finally in 1st try block");
}
}
}
程序运行结果如下:
Entering first try block
Entering second try block
finally in 2nd try block
Caught MyoneException in 1st try block
finally in 1st try block
(4)创建用户异常类:
如果 Java 提供的系统异常类型不能满足程序设计的需求,我们可以设计自己的异常类型。
从 Java 异常类的结构层次可以看出,Java 异常的公共父类为 Throwable 。在程序运行中可能出现两种问题:一种是由硬件系统或JVM导致的故障, Java 定义该故障为 Error 。这类问题用户程序不
能够处理的。另外一种问题是程序运行错误,Java 定义为 Exception。这种情况下,可以通过程序设计的调整来实现异常处理。
因此,用户定义的异常类型必须是 Throwable 的直接或间接子类。Java 推荐用户的异常类型以 Exception 为直接父类。创建用户异常的方法如下:
class UserException extends Exception{
UserException(){
super();
…… //其它语句
}
}
我们在使用异常时,有以下几点建议需要注意:
(1) 对于运行时例外,如果不能预测它何时发生,程序可以不做处理,而是让Java虚机去处理它。
(2) 如果程序可以预知运行时例外可能发生的地点和时间,则应该在程序中进行处理,而不应简单地把它交给运行时系统。(3)在自定义异常类时,如果它所对应的异常事件通常总是在运行时产生的,而
且不容易预测它将在何时、何处发生,则可以把它定义为运行时例外,否则应定义为非运行时例外。
用户定义的异常类的使用
// Exceptionjava
class MyotherException extends Exception { //用户定义的异常
p lic MyotherException() {
}
p lic MyotherException(String msg) {
super(msg);
}
}
p lic class Exception8 {
p lic static void f() throws MyotherException {
System.out.println("Throwing MyotherException from f()");
throw new MyotherException();
}
p lic static void g() throws MyotherException {
System.out.println("Throwing MyotherException from g()");
throw new MyotherException("Originated in g()");
}
p lic static void main(String[] args) {
try {
f();
}catch (MyotherException e) {
e.printStackTrace();
}
try {
g();
} catch (MyotherException e) {
e.printStackTrace();
}
}
}
程序的运行结果如下:
Throwing MyotherException from f()
Throwing MyotherException from g()
MyotherException
at Exceptionf(Exceptionjava:13)
at Exceptionmain(Exceptionjava:21)
MyotherException: Originated in g()
at Exceptiong(Exceptionjava:17)
at Exceptionmain(Exceptionjava:26)