Alpha's Tech Space

一个关于软件开发的技术交流空间,希望和大家共同进步!(.net/C/C++/Java)

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Java异常处理:

 

什么是异常:

异常可以看做是运行时的错误,不正常的操作产生的结果等,可能来源于I/O错误,设计逻辑的错误等。

异常处理就是一种用于方便捕捉并进入适当处理过程的手段,可以方便我们的程序设计。

方法:

try{} catch{} finally{} 组合来捕获并处理异常。

 

其中,try{}用于定义可能产生异常的代码段,catch{}部分是用来处理异常的,而finally{}部分则做一些清理工作。其处理的逻辑是:

1 try中语句在执行中如果没有发生异常,则转到 后继续运行

2 try段中一旦某一条语句发生了异常,则在try段其余的语句不会被继续执行,而是立即转到catch部分进行异常处理。

3 finally段是一个特殊的地方,其中的代码一定会被执行:在没有发生异常的情况下,在try块之后执行;发生异常后,则在符合条件的catch语句段后执行!切记!

4 finally段一般用于完成关闭文件,释放网络套接字,或者其它清理工作。

5 try{} catch{} finally{} 组合中,以下情况都是真确的:

  try{} catch{} finally{}       完全版本

try{} catch{}                 没有finally{}

  try{} finally{}               没有catch{}

但是,如果没有catch{} finally{} ,而只有try{},这时Java语法所不允许的。

注意:

try catch 代码段是不可分割的;

对一个catch 代码段,可以存在多个catch 部分,分别用来捕获不同类型的异常,这些catch部分也不能分开;

 

接下来,我们来思考一个问题:当我们采用try{} finally{}组合时,程序如何来处理异常呢?是的,我们没有提供catch{}代码段,那么在异常发生以后,我们没有处理,这个异常怎么样了?答案是,这个异常被转给了该方法的调用者。应该了解的一个常识是,在执行过程方法调用的时候,计算机实际上会做一个保护调用环境,并将其压栈的工作,然后才转到被调用的过程和方法的入口开始执行;执行完毕,将栈中保存的环境进行恢复,回到调用者调用该方法的语句,继续执行。为什么要说这个呢,因为我们的问题和这个背景知识有密切的关系:异常被转移了。这个机制被成为:异常传递机制。如果调用者也不处理,那么异常就继续沿调用顺序继续被传递,直至到达main()函数,如果main()还是不处理,那只能由JVM站出来了,最后的结果就是屏幕上出现一些错误信息:告诉用户,应用发生了异常,是从哪里开始发生的,等等……,我现在没有办法继续工作了。(谁让你们都不处理的?)

 

Java的异常捕获顺序

JavaException 有很多种类,在Java1.4api document中,列出的就有:

Direct Known Subclasses:

AclNotFoundException, ActivationException, AlreadyBoundException, ApplicationException, AWTException, BackingStoreException, BadLocationException, CertificateException, ClassNotFoundException, CloneNotSupportedException, DataFormatException, DestroyFailedException, ExpandVetoException, FontFormatException, GeneralSecurityException, GSSException, IllegalAccessException, InstantiationException, InterruptedException, IntrospectionException, InvalidMidiDataException, InvalidPreferencesFormatException, InvocationTargetException, IOException, LastOwnerException, LineUnavailableException, MidiUnavailableException, MimeTypeParseException, NamingException, NoninvertibleTransformException, NoSuchFieldException, NoSuchMethodException, NotBoundException, NotOwnerException, ParseException, ParserConfigurationException, PrinterException, PrintException, PrivilegedActionException, PropertyVetoException, RefreshFailedException, RemarshalException, RuntimeException, SAXException, ServerNotActiveException, SQLException, TooManyListenersException, TransformerException, UnsupportedAudioFileException, UnsupportedCallbackException, UnsupportedFlavorException, UnsupportedLookAndFeelException, URISyntaxException, UserException, XAException

 

很多吧。在我们的程序中,有两个不可能是我们要知道的:

我们的程序不可能处理所有的异常;

我们的程序也不可能产生所有的异常;

 

我们只关心也只可能关心其中一部分我们感兴趣的种类。

前面我们也提到了,一个try{} 代码段可以有多个catch{},那么我们怎么安排这多个catch{}呢?原则:先特殊,再一般。就是说,先处理子类异常,再处理父类异常。可能不太好理解,举个例子:

public class  ReadFile

    public static void main()

   

        try

{

    //some statement which concerning about reading files.

}

catch(FileNotFoundException e)

{

    System.err.println(“File not fond.”);

    System.err.println(e.getMessage());

    e.printStackTrace();

}

catch(IOException e)

{

    System.err.println(“I/O error.”);

    System.err.println(e.getMessage());

    e.printStackTrace();

}

 

为什么把处理FileNotFoundException的部分放在IOException 前面?还记得前面说的原则吗?在有多个catch段的情况下,先子类后父类。从api document中我们可以找到这样的描述:

Class FileNotFoundException

java.lang.Object

  java.lang.Throwable

      java.lang.Exception

          java.io.IOException

              java.io.FileNotFoundException

 

看到了吗?FileNotFoundException是从IOException继承来的。这下明白了吧。

如果我们把这个顺序反过来会怎么样呢?你可以自己试试看。

结果是什么?Java编译器报错了:……Exception  has already been caught.

 

抛出和处理的博弈:

我们的程序中,我们出了要处理异常,也会抛出异常。Java规定,如果我们自己的方法需要抛出异常,我们就有责任来告诉编译器,这就是抛出异常声明,除非,要抛出的异常是RunimeException的子类。

对这个问题的详细解释是:

1 Java中的异常分为两种:一种是检查异常,一种是非检查异常。非检查异常指RuntimeException 或者Error 及它们的子类。

2 检查异常需要被捕获和处理;非检查异常则不必。

3 Java要求每一个用Java语言来实现的方法,或者以catch{}代码段来捕获检查异常,或者在方法的声明语句中告诉编译器,自己要传递检查异常(把异常抛出),其格式是

    public MethodName() throw **Exception

{

    //statements

}

其中 **的意思是指定某一类的异常。

4 还有一种特殊情况,尽管我们提供了catch{}代码段来处理异常,但处理的方式是把异常继续抛出,那么我们就必须同时通知编译器,自己可能要把异常抛出。这是一个极端的例子。

 

总结下来,就是说,对于检查异常,我们要不自己处理,要不就声明自己可能抛出异常,否则,编译报错。是不是象捕获处理和抛出的博弈呢?

 

反思Java编译器的抱怨

Java编译器为设么会这么设计?这和Java的设计逻辑有关。根据它的设计思想,使程序正常工作使开发人员的责任。为了达到这一个目的,Java某种程度上强制用户去关心错误的处理(捕获,处理异常),这样,异常处理的部分就成为程序的一个组成部分了,这也从很大程度上帮助增强了Java程序的稳定性和健壮性。

 

但是,我们看到这样的程序了吗:

        try

{

    //some statement

}

catch(Exception e)  {}

什么感想?人类真“伟大”,这真是“上有政策,下有对策” Java语言中的典型体现啊!不错,编译通过了,可这和编译器被设计有这样要求的初衷却相去遥远了。我们聪明的欺骗了可怜的智能有限的编译器,我们自己呢?除了一时的方便之外,我们得到什么了呢?

 

说明:从这部分开始,我同时参考了另外一本Java 教材:《Java2学习指南》,以丰富笔记的内容,并通过对比的方式,从不同的角度来更好的理解知识点。

我个人的感觉,这是一本很不错的教程,内容组织合理全面,基本覆盖到了java 语言的各主要内容,翻译也很好,语句通顺,表意明了。推荐各位有空阅读。

书籍信息:

Java2学习指南 (美)贝茨等著 袁鹏飞等译 人民邮电出版社 2004.01出版 ISBN 7-115-11803-5

English Name: Sun Certified Programmer Developer for Java2   Bates. B.

 

 

 

相关文章:
读书笔记:《Java2 教程》(一)

读书笔记:《Java2 教程》(

读书笔记:《Java2 教程》(三)

读书笔记:《Java2 教程》(四)

读书笔记:《Java2 教程》(五)

读书笔记:《Java2 教程》(六)

读书笔记:《Java2 教程》(七)

 

posted on 2005-06-08 16:28  Lexrate  阅读(966)  评论(0)    收藏  举报