`

第十二章 通过异常处理错误

阅读更多
2013年7月10日 星期三 00时04分21秒

第十二章  通过异常处理错误

12.1 概念
        Java的基本理念是“结构不佳的代码不能运行”
        Java使用异常来提供一致的错误报告模型,使得构件能够与客户端代码可靠地沟通问题。       

12.2 基本异常
        异常情形(Exceptional condition)是指阻止当前方法或作用域继续执行的问题。
        当抛出异常后,有几件事会随之发生:
                首先:同Java中其他对象一样,将使用new在堆上创建异常对象。
                然后:当前的执行路径被终止,并且从当前环境中弹出对异常对象的引用。此时异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理                程序。

        12.2.1 异常参数   
                与使用Java中的其他对象一样,我们总是使用new在堆上创建异常对象,这也伴随着存储空间的分配和构造器的使用。所以标准异常类都有两个构造器:一个默认构造器,一个棘手                字符串作为参数,以便把相关信息放入异常对象的构造器:    
                        throw new NullPointerException("t=null");              

12.3 捕获异常
                要明白异常是如何被捕获的,首先理解监控区域(guarded region)的概念。
        12.3.1 try块
                捕获异常: try{  //     }

        12.3.2 异常处理程序
                抛出的异常必须在某处得以处理。这个“地点”就是异常处理程序。用关键字catch表示:
                                try{
                                   }catch(){
                                   }catch(){ }
                终止与恢复
                        异常处理理论上有两种基本模型:
                                        1) Java支持终止模型
                                        2) 恢复模型
                               
12.4 创建自定义异常
                程序员可以自己定义异常类来表示程序中可能会遇到的特定的问题。
                要自己定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承。
                                    package chapter12;
/*@name PriorityQueueDemo.java
* @describe  12.4 创建自定义异常
* @since 2013-07-10 0:45
* @author 张彪
*/
class SimpleException extends Exception{}
public class InheritingException {
        public void f() throws SimpleException{
                System.out.println("Throw SimpleException from f();");
                throw new SimpleException();
        }
        public static void main(String[] args) {
                InheritingException sed=new InheritingException();
                try {
                        sed.f();
                } catch (SimpleException e) {
                        System.out.println("Caught it !");
                }
        }
}
/*Throw SimpleException from f();
Caught it !*/

===============================================================================
package chapter12;
class MyException extends Exception{
        public MyException(){}
        public MyException(String msg){super(msg);}
}

public class FullConstructors {
        public static void f()throws MyException{
                System.out.println("Throwing MyException from f();");
                throw new MyException();
        }
        public static void g()throws MyException{
                System.out.println("Throwing MyException from g();");
                throw new MyException("Originated in g()");
        }
        public static void main(String[] args) {
                try {
                        f();
                } catch (MyException e) {
                        e.printStackTrace(System.out);
                }
                try {
                        g();
                } catch (MyException e) {
                        e.printStackTrace(System.out);
                }
        }
}

/*
Throwing MyException from f();
chapter12.MyException
        at chapter12.FullConstructors.f(FullConstructors.java:11)
        at chapter12.FullConstructors.main(FullConstructors.java:19)
Throwing MyException from g();
chapter12.MyException: Originated in g()
        at chapter12.FullConstructors.g(FullConstructors.java:15)
        at chapter12.FullConstructors.main(FullConstructors.java:24)*/

                在异常处理中,调用了throwable类(Exception即从此类继承)的printStackTrace()方法。它将打印“从方法调用处直到异常抛出处”的方法调用序列。

        12.4.1 异常与记录日志      
                使用java.util.logging工具将输出记录到日志中。
                                      package chapter12;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
/*@name LoggingException.java
* @describe  12.4.1 异常与记录日志
* @since 2013-07-10 0:59
* @author 张彪
*/
class LoggingException extends Exception{
        private static Logger logger=Logger.getLogger("LoggingException");
        public LoggingException(){
                StringWriter trace=new StringWriter();
                printStackTrace(new PrintWriter(trace));
                logger.severe(trace.toString());
        }
}
public class LoggingExceptions {
        public static void main(String[] args) {
                try {
                        throw new LoggingException();
                } catch (LoggingException e) {
                        System.out.println("Caught"+e);
                }
                try {
                        throw new LoggingException();
                } catch (LoggingException e) {
                        System.out.println("Caught"+e);
                }
        }
}

/*2013-7-10 1:07:56 chapter12.LoggingException <init>
严重: chapter12.LoggingException
        at chapter12.LoggingExceptions.main(LoggingExceptions.java:23)

Caughtchapter12.LoggingException
Caughtchapter12.LoggingException
2013-7-10 1:07:56 chapter12.LoggingException <init>
严重: chapter12.LoggingException
        at chapter12.LoggingExceptions.main(LoggingExceptions.java:28)*/

                    这个Logging对象会将其输出发送到System.err。    为了产生日志记录消息,我们欲获取异常抛出处的栈轨迹。
                     
                    更常见的情况是我们需要捕获和记录其他人编写的异常,异常我们必须在异常处理程序中生成日志消息。
                                      package chapter12;
class MyException2 extends Exception{
        private int x;
        public MyException2(){}
        public MyException2(String msg){super(msg);}
        public MyException2(String msg,int x){
                super(msg);
                this.x=x;
        }
        public int val(){
                return x;
        }
        public String getMessage(){
                return "Detail Message:"+x+ " "+super.getMessage();
        }
}

public class ExtraFeatures{
        public static void f() throws MyException2{
                System.out.println("Throwing MyException2 from f()");
                throw new MyException2();
        }
        public static void g() throws MyException2{
                System.out.println("Throwing MyException2 from g()");
                throw new MyException2("Originated in g()");
        }
        public static void h() throws MyException2{
                System.out.println("Throwing MyException2 from h()");
                throw new MyException2("Originated in g()",47);
        }
        public static void main(String[] args) {
                try {
                        f();
                } catch (MyException2 e) {
                        e.printStackTrace(System.out);
                }
                try {
                        g();
                } catch (MyException2 e) {
                        e.printStackTrace(System.out);
                }
                try {
                        h();
                } catch (MyException2 e) {
                        e.printStackTrace(System.out);
                }
        }
}
          
12.5 异常说明
                Java鼓励热门把方法可能抛出的异常告知使用此方法的客户端程序员。这是种优雅的做法。异常说明属于方法说明的一部分。紧跟在形式参数的列表后面。
                异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表,所以方法定义可能如下:
                                void f() throws TooBig, TooSmall, DivZero{ //  .....}
               
12.6 捕获所有异常
                可以通过异常基类Exception来捕获异常处理程序中所有的异常。
                下面展示Exception的用法:
                                        package chapter12;
public class ExceptionMethods{
        public static void main(String[] args) {
                try {
                        throw new Exception("My Exception");
                } catch (Exception e) {
                        System.out.println("Caught Exception");
                        System.out.println("e.getMessage()="+e.getMessage());
                        System.out.println("e.getLocalizedMessage()="+e.getLocalizedMessage());
                        System.out.println("toString()"+e);
                        System.out.println("printStackTrace()");
                        e.printStackTrace(System.out);
                }
        }
}

        12.6.1 栈轨迹
                        printStackTrace()方法所提供的信息可以通过getStackTrace()方法直接访问。这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。
                        下面是简单演示:
                                             package chapter12.exceptions;
/*@name WhoCalled.java
* @describe  12.6.1 栈轨迹
* @since 2013-07-13 02:45
* @author 张彪
*/
public class WhoCalled {
        static void f(){
                try {
                        throw new Exception();
                } catch (Exception e) {
                        for(StackTraceElement t: e.getStackTrace()){
                                System.out.println(t.getMethodName());
                        }
                }
        }
        static void g(){f();}
        static void h(){g();}
        public static void main(String[] args) {
                f();
                System.out.println("-------------------");
                g();
                System.out.println("-------------------");
                h();
        }
}

/*f
main
-------------------
f
g
main
-------------------
f
g
h
main*/
       
                                元素0是栈顶元素,并且是调用序列中的最后一个方法调用。数组中的最后一个元素和栈底是调用序列中的第一个方法调用。                                   
        12.6.2 重新抛出异常
                        有时候希望把刚捕获的异常重新抛出。如下:
                                        catch (Exception e)
{
                 System.out.println("An exception was thrown");
                 throw e;
           }       
                            
                        如下例子:
                                   package chapter12.exceptions;
/*@name WhoCalled.java
* @describe  12.6.2 重新抛出异常
* @since 2013-07-13 03:09
* @author 张彪
*/
public class ReThrowing {
        public static void f() throws Exception{
                System.out.println("originating the exception in f()");
                throw new Exception("thrown from f()");
        }
        public static void g() throws Exception{
                try {
                        f();
                } catch (Exception e) {
                        System.out.println("Inside g(),e.printStackTrace()");
                        e.printStackTrace(System.out);
                        throw e;
                }
        }
        public static void h() throws Exception{
                try {
                        f();
                } catch (Exception e) {
                        System.out.println("Inside h(),e.printStackTrace()");
                        e.printStackTrace(System.out);
                        throw (Exception)e.fillInStackTrace();
                }
        }
        public static void main(String[] args) {
                try {
                        g();
                } catch (Exception e) {
                        System.out.println("main:printStackTrace()");
                        e.printStackTrace(System.out);
                }
                System.out.println("---------");
                try {
                        h();
                } catch (Exception e) {
                        System.out.println("main:printStackTrace()");
                        e.printStackTrace(System.out);
                }
        }
}             
                       
                           调用fillInStackTrace()的那一行就成了异常的新发生地了。即原来的异常发生点信息丢失了,剩下的是与新的抛出点有关的信息。
                       
                           永远不必为清理一个异常而担心,他们都是用New在堆上创建的对象,所以垃圾回收器会自动把他们清理掉。

        12.6.3 异常链               
               
12.7 Java标准异常
                Throwable这个Java类被用来表示任何可以作为异常被抛出的类。 分为Error 和Exception。
        12.7.1 RunException
                运行时异常会自动被Java虚拟机抛出
                如果RunException没有被捕获而直达main()方法,那么在程序退出前将调用异常的printStackTrace()方法。

12.8 使用finally进行清理
                如果希望无论try块中的异常是否抛出,它们都能得到执行,那么可以在异常处理程序后面加上finally子句。
                try{}
                catch(){}
                finaly{}
                可以将try块放在循环中,这样就建立了一个“程序继续执行之前必须要达到”的条件。
        12.8.1 finally用来做什么
                当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。
        12.8.2 在return中使用finally
                              package chapter12.exceptions;
/*@name MultipleReturn.java
* @describe  12.8.2 在return中使用finally
* @since 2013-07-28 10:27
* @author 张彪
*/
public class MultipleReturn {
        public static void f(int i){
                System.out.println("Initialization that requires cleanup");
                try {
                        System.out.println("Point 1");
                        if(i==1) return;
                        System.out.println("Point 2");
                        if(i==2) return;
                        System.out.println("Point 3");
                        if(i==3) return;
                        System.out.println("Point 4");
                        if(i==4) return;
                } finally {
                        System.out.println("Performing cleanup");
                }
        }
        public static void main(String[] args) {
                for (int i = 1; i < 4; i++) {
                        MultipleReturn.f(i);
                }
        }
}

/*Initialization that requires cleanup
Point 1
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Point 3
Performing cleanup*/    

                   可以发现finally中的方法会在return之前被执行。
                                         
        12.8.3 缺憾:异常丢失
                用某些特殊方法使用finally子句时,有些异常可以轻易的被忽略掉。
                                    package chapter12.exceptions;
class VeryImportantExcepton extends Exception{
        public String toString(){
                return "A very impotant exception";
        }
}
class HoHumException extends Exception{
        public String toString(){
                return "A trivial exception";
        }
}
public class LostMessage {
        void f() throws VeryImportantExcepton{
                throw new VeryImportantExcepton();
        }
        void dispose() throws HoHumException{
                throw new HoHumException();
        }
        public static void main(String[] args) {
                try{
                        LostMessage ls=new LostMessage();
                        try{
                                ls.f();
                        }finally{
                                ls.dispose();
                        }
                }catch (Exception e) {
                        System.out.println(e);
                }
        }
}
/*A trivial exception*/         

从打印出的信息可以看出VeryImportantExcepton不见了。     

                                一种更加简单丢失异常的方式是从finally中返回:
                                           public class Exceptionilencer {
   public static void main(String[] args) {
          try {
                throw new RuntimeException();
          } finally{
              //using 'return' inside the finally block will slience any thrown exception
             return;
          }
     }
}                               

12.9 异常限制
                当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。

12.10 构造器
            如果构造器内抛出了异常,这些清理行为也许就不能正常工作了。这意味着写构造器时要格外细心。                   
                                          package chapter12.exceptions;
/*@name CleanupIdioom.java
* @describe  12.10 构造器  ,注意构造器时异常的处理
* @since 2013-08-01 20:15
* @author 张彪
*/
class NeedCleanup{
        private static long counter=1;
        private final long id=counter++;
        public void dispose(){
                System.out.println("NeedCleanup "+id+" dispose");
        }
}
class ConstructionException extends Exception{}

class NeedCleanup2 extends NeedCleanup{
        public NeedCleanup2() throws ConstructionException{}
}

public class CleanupIdioom {
        public static void main(String[] args) {
                NeedCleanup nc1=new NeedCleanup();
                try {       
                }finally {
                        nc1.dispose();
                }
                //select2
                NeedCleanup nc2=new NeedCleanup();
                NeedCleanup nc3=new NeedCleanup();
                try{
                       
                }finally{
                        nc3.dispose();
                        nc2.dispose();
                }
                //select3
                try {
                        NeedCleanup2  nc4= new NeedCleanup2();
                        try {
                                NeedCleanup2  nc5= new NeedCleanup2();
                        } finally{
                        }
                } catch (Exception e) {
                        // TODO: handle exception
                }
               
        }
}
          
12.11 异常匹配
                抛出异常时,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到后,将不再继续查找。
                派生类的对象也可以匹配其基类的处理程序。
                                 package chapter12.exceptions;
class Annoyance extends Exception{}
class Sneeze extends Annoyance{}
public class Human {
        public static void main(String[] args) {
                try {
                        throw new Sneeze();
                } catch (Sneeze s) {
                        System.out.println("Caught Sneeze");
                }catch (Annoyance a) {
                        System.out.println("Caught Annoyance1");
                }
                try {
                        throw new Sneeze();
                } catch (Annoyance e) {
                        System.out.println("Caught Annoyance2");
                }
        }
}
/*Caught Sneeze
Caught Annoyance2*/

                 //如果把捕获基类的catch子句放在最前面,依次想把派生类的异常给“屏蔽”掉,就像这样:
                                   try {
       throw new Sneeze();
       } catch (Annoyance s) {
             System.out.println("Caught Sneeze");
       }catch (Sneeze a) {
                        System.out.println("Caught Annoyance1");
       }
  }

                这样编译器会发现Sneeze的catch子句永远也得不到执行,因此它会向你报告错误。

12.12 其它可选方式
            事实上,异常处理的一个重要目标是将错误处理的代码和错误发生的地点相分离。
            “被检查的异常”及其并发症,以及采用什么方法解决该问题。
            12.12.1 历史             
            12.12.2 观点
            12.12.3 把异常传递给控制台
            12.12.4 把“被检查的异常”转换为“不检查的异常”
12.13 异常使用指南
12.14 总结



                                                                                                        2013-08-01 21:02 记 @jinrongdajie31.xichengqu.beijing





0
1
分享到:
评论
2 楼 listen-raining 2013-08-20  
freezingsky 写道
异常虽好,但永远不要想着用异常来控制流程。应该把这句话加上去。



是的! 本人初学,谢谢指点!
1 楼 freezingsky 2013-08-01  
异常虽好,但永远不要想着用异常来控制流程。应该把这句话加上去。

相关推荐

    Java编程思想第十二章通过异常处理错误.pptx

    Java编程思想第十二章通过异常处理错误.pptx

    Java高级程序设计(第二版)--第2章-异常处理.pptx

    Java高级程序设计(第二版)--第2章-异常处理全文共19页,当前为第12页。 知识点:自定义异常、throw关键字 一、自定义异常 &lt;class&gt; &lt;自定义异常类名&gt; &lt;extends&gt; &lt;Exception&gt;{ // 变量、构造方法、成员方法 } 二、...

    第12章 错误调试和异常处理.ppt

    C#入门ppt,一共16章

    编程思想下篇

    由于上传文件大小限制...第12章 通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面

    Thinking in java4(中文高清版)-java的'圣经'

    非静态实例初始化 5.8 数组初始化 5.8.1 可变参数列表 5.9 枚举类型 5.10 总结 第6章 访问权限控制 第7章 复用类 第8章 多态 第9章 接口 第10章 内部类 第11章 持有对象 第12章 通过异常处理错误 第13章 字符串 第...

    完整版 Cortex-M3与Cortex-M4权威指南

    第12章 错误异常和错误处理 第13章 浮点运算 第14章 调试和跟踪特性 第15章 Keil ARM微控制器开发套件入门 第16章 IAR Embedded Workbench for ARM入门 第17章 GCC入门 第18章 输入和输出软件实例 第19章 使用嵌入式...

    PHP程序设计第2版

    第12章 日期和时间 第13章 PHP 5.1 第14章 表单和导航提示 第15章 处理文件上传 第16章 网络 第17章 PHP和LDAP 第18章 会话处理器 第19章 用Smarty模板化 第20章 Web服务 第21章 安全PHP编程 第22章 SQLite 第23章 ...

    thinkinjava源码-Thinking-in-Java:ThinkingInJava源代码和练习题

    通过异常处理错误 第13章 字符串 第14章 类型信息 第15章 泛型 第16章 数组 第17章 容器深入研究 第18章 Java I/O系统 第19章 枚举类型 第20章 注解 第21章 并发 第22章 图形化用户界面 水平有限,发现错误不适者,...

    php教程(ppt)

    第一章PHP简介及环境配置.ppt 第二章PHP的基本语法.ppt 第三章其他操作符与表达式、流程控制.ppt 第四章字符串处理.ppt ...第十二章面向对象II.ppt 第十三章面向对象的数据库操作.ppt 第十四章错误和异常.ppt

    深入理解计算机系统(英文版)

    第1章 计算机系统漫游 第一部分 程序结构和执行  第2章 信息的表示和处理  第3章 程序的机器级表示  第4章 处理器体系结构  第5章 优化程序性能 ... 第12章 并发编程 附录a 错误处理 参考文献

    21天学通c++ 中文 第五版

    第12章 实现继承 第13章 管理数组和字符串 第14章 多态 第2周复习 第3周课程简介 第15章 特殊类和函数 第16章 高级继承 第17章 处理流 第18章 创建和使用名称空间 第19章 模板 第20章 处理错误和异常 第21...

    PHP程序设计(第2版) 英文

    PEAR 第12章 日期和时间 第13章 PHP 5.1&lt;br&gt;第14章 表单和导航提示 第15章 处理文件上传 第16章 网络 第17章 PHP和LDAP 第18章 会话处理器 第19章 用Smarty模板化 第20章 Web服务 第21章...

    C++程序设计原理与实践高清版.part2(1)

    第3章 对象、类型和值 第4章 计算 第5章 错误 第6章 编写一个程序 第7章 完成一个程序 第8章 函数相关的技术细节 第9章 类相关的技术细节 第二部分 输入和输出 第10章 输入/输出流 第11章 定制输入/输出 第12章 一...

    C++程序设计原理与实践高清版.part1(1)

    第3章 对象、类型和值 第4章 计算 第5章 错误 第6章 编写一个程序 第7章 完成一个程序 第8章 函数相关的技术细节 第9章 类相关的技术细节 第二部分 输入和输出 第10章 输入/输出流 第11章 定制输入/输出 第12章 一...

    PHP与MySQL程序设计(带完整书签)

    第8 章 错误和异常处理 134 第9 章 字符串和正则表达式 146 第10 章 处理文件和操作系统 176 第11 章 pear 197 第12 章 日期和时间 205 第13 章 处理html 表单 219 第14 章 身份验证 231 第15 章 处理文件上传 244 ...

    从零开始学JavaScript 源代码

    第一篇 JavaScript入门篇 第1章 初步了解JavaScript ...第12章 JavaScript与Ajax技术 第13章 JavaScript错误与异常处理 第三篇 JavaScript实例篇 第14章 JavaScript与插件通信 第15章 JavaScript常用特效收集

    Python程序设计.rar

    第12章 图形用户界面 第13章 图形绘制 第14章 (1)Math、random、日期时间模块 第14章 (2)Numpy科学计算与matplotlib可视化 第15章 字符串、正则表达式、网络爬虫 第16章 文件和数据交换 第17章 数据库访问 第18章 ...

    《C++:程序设计原理与实践》

    第12章 一个显示模型 第13章 图形类 第14章 设计图形类 第15章 绘制函数图和数据图 第16章 图形用户界面 第三部分 数据结构和算法 第17章 向量和自由空间 第18章 向量和数组 第19章 向量、模板和异常 第20章 容器和...

    易学C++高清完整pdf版

    第12章 编写程序技巧 150 第三篇 面向对象的程序设计 第13章 初识对象 163 第14章 再识对象 169 第15章 造物者与毁灭者——对象生灭 178 第16章 共有财产·好朋友·操作符 206 第17章父与子——继承 228...

Global site tag (gtag.js) - Google Analytics