`

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

阅读更多
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

    精品课件 Python从入门到精通 第12章 异常处理及程序调试(共10页).ppt

    本课件的第12章主要关注如何处理这些异常以及如何进行程序调试,以确保代码能够正确执行。 异常处理在Python中通过使用`try-except`语句来实现。当`try`块中的代码出现异常时,程序会立即跳转到相应的`except`块中...

    Java语言程序设计基础第十版第十二章课后复习题答案

    根据提供的信息,我们可以总结出以下关于《Java语言程序设计基础第十版》第十二章的一些关键知识点及解答: ### 一、异常处理基本概念 #### 12.1 **问题:** 异常处理的主要思想是什么? **解答:** 异常处理的主要...

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

    C#入门ppt,一共16章

    大数据分析与挖掘课程 数据挖掘(第三版)教程 纯英文原版PPT课件 第12章 Outlier-异常处理 共56页.pptx

    总之,《数据挖掘:概念与技术》(第三版)第12章深入介绍了异常处理的相关理论和技术,并提供了丰富的案例研究。通过对异常处理的学习,可以更好地理解如何从复杂多变的数据集中发现有价值的信息,这对于提高数据...

    第12章 python文件存取和异常处理-python基础,快速入门.pptx

    在Python中,文件存取是编程中常见的操作,它涉及文件的读取、写入以及异常处理。本章主要讲解了如何使用Python进行文件操作。 1. **文件读取** - 使用`open()`函数打开文件,例如`open('work\readme.txt')`。在...

    完整版 Java开发实训课程系列-SpringMVC框架技术教程 第6章 异常处理 (共12页).pptx

    **SpringMVC框架中的异常处理**是Java开发中不可或缺的一部分,它确保了应用程序在遇到错误时能够优雅地处理并提供有用的反馈。在SpringMVC框架中,异常处理机制允许开发者集中处理可能出现的各种异常,提高代码的可...

    Java编程思想笔记(全)

    #### 第 12 章 通过异常处理错误 第十二章讲解了Java中的异常处理机制。异常处理是处理程序运行时错误的有效方式。本章首先介绍了异常的概念,然后详细讨论了如何使用try-catch-finally语句来捕获和处理异常,以及...

    编程思想下篇

    由于上传文件大小限制...第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章 字符串 第...

    c++primer第五版习题答案(第12章)

    通过解决这些习题,学生可以加深对C++语言特性的理解,包括类和对象、动态内存管理、模板、异常处理、标准模板库(STL)等。 在实际使用这些习题答案时,建议读者首先尝试独立完成习题,遇到问题后再参照答案进行核对...

    05_第5章 异常1

    在Java编程中,异常处理...通过正确使用异常处理,开发者可以更有效地定位和解决程序中的错误,提高程序的稳定性和用户体验。同时,自定义异常允许开发者根据项目需求创建特定的异常类型,使代码更具可读性和可维护性。

    jsp程序开发范例宝典 第十二章

    《JSP程序开发范例宝典》第十二章聚焦于JSP(Java Server Pages)的高级应用和实战技巧。在这一章中,我们通常会深入探讨以下关键知识点: 1. **自定义标签库(Custom Tags)**:JSP 2.0引入了自定义标签库的概念,...

    GD32-STM32-CH32芯片内核ARM Cortex-M3与Cortex-M4权威指南(中文)

    第十二章 错误异常和错误处理 第十三章 浮点运算 第十四章 调试和跟踪测试 第十五章 Keil ARM 微控制器开发套件入门 第十六章 IAR Embedded Workbench for ARM 入门 第十七章 GCC 入门 第十八章 输入和输出软件实例 ...

    java开发实战经典第十二章课后习题答案

    《Java开发实战经典》是许多Java初学者的重要学习资料,其第十二章涵盖了诸多关键知识点。这章可能涉及了面向对象编程的深入理解、异常处理、集合框架的使用、多线程、网络编程或者IO流等内容。课后习题通常用于巩固...

    北大青鸟S1JAVA第十二章课后练习详解

    在北大青鸟S1JAVA课程的第十二章中,学员们会接触到一系列深入的Java编程概念和实践。这一章节的课后练习旨在巩固所学知识,提升编程技能。以下是本章涉及的一些关键知识点: 1. **面向对象编程基础**:在Java中,...

    Java语言程序设计:第五章 异常.ppt

    本章主要介绍了Java中的异常处理机制及其重要性。 异常是程序运行时遇到的问题,如文件错误、设备故障、逻辑错误等。Java提供了一套完整的异常处理框架,使得开发者可以有效地管理和响应这些异常情况。异常处理的...

    Java语言基础入门教程 Java开发编程基础课程 第12章 内部类和异常处理 共18页.pptx

    异常处理是Java编程中的一个重要概念,它允许程序员在程序运行过程中处理错误情况,避免程序崩溃。 - **基本语法**: ```java try { // 可能抛出异常的代码 } catch (ExceptionType1 e1) { // 处理异常 } ...

Global site tag (gtag.js) - Google Analytics