zoukankan      html  css  js  c++  java
  • 第12章 通过异常处理错误

    第12章 通过异常处理错误

    java的基本理念是”结构不佳的代码不能运行“

    通过这种方式可以使得你更加自信:你的应用中没有未处理的错误。异常处理时Java中唯一正式的错误报告机制,并且通过编译器强制执行。

    12.1 概念

    把”描述再正常执行过程中做什么事“的代码和”出了问题怎么办的代码相分离“异常使代码相分离。

    12.2 基本概念

    当抛出异常后

    1. 使用new再堆上创建异常对象
    2. 当前执行路径(他不能继续下去)被终止,并且从当前环境中弹出对异常对象的引用
    3. 异常处理机制接管程序
    4. 寻找一个恰当的地方(异常处理程序)继续执行程序
    5. 将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去

    12.2.1 异常参数

    用new再堆上创建异常对象,伴随着存储空间的分配和构造器的调用。所有标准异常类都有两个构造器:

    • 一个是默认构造器
    • 另一个接受字符串为参数,以便能把相关信息放入异常对象的构造器

    Throw new NullPointerException("t = null");

    new创建了异常对象之后,此对象的引用将传给throw。尽管返回的异常对象其类型通常与方法设计的返回类型不同,但从效果上看,它就像使从方法”返回“的。

    12.3 捕获异常

    监控区域(guarded region)的概念

    12.3.1 try块

    方法内部抛出异常(方法内部调用其他的方法抛出了异常)

    12.3.1 异常处理程序

    紧跟在try后,以关键字catch表示(看起来就像是接受一个且仅接受一个特殊类型的方法 )

    • 当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序

    • 然后进入catch子句执行,此时认为异常得到了处理

    • catch子句结束,处理程序的查找过程结束

    • 只有匹配的catch才能执行

      终止与恢复

    异常处理理论上有两种基本模型。

    • Java支持终止模型(它是Java和C++所支持的模型)。在这种模型中,一旦异常被抛出,就表明错误已经无法挽回,也不能回来继续执行。
    • 恢复模型。异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。对于恢复模型,通常希望异常被处理之后能继续执行程序。
    • 如果Java像实现类似恢复的行为,那么在遇见错误时就不能抛出异常,而是调用方法来修正该错误,或者把try放进while循环里,这样就不断地进入try块,直到得到满意的结果。
    • 恢复性异常 导致耦合,恢复性程序需要了解异常抛出的地点 , 这势必要包含依赖于抛出位置的非通用性代码。

    12.4 创建自定义异常

    自己定义异常类来表示程序中可能遇到的特定问题,最好是选择意思相近的异常类继承。

    练习1

    /* Create a class with a main(0 that throws an object of class Exception
     * inside a try block. Give the constructor for Exception a String argument.
     * Catch the exception inside a catch clause and print the String argument.
     * Add a finally clause and print a message to prove you were there.
     */
    
    class Exception1 extends Exception {
        public Exception1(String msg) {
            super(msg);//super构造基类构造器
            System.out.println("Exception1(String msg)");
        }
    }
    
    public class No1Ex {
        public static void f() throws Exception1 {
            System.out.println("Throwing MyException from f()");
            throw new Exception1("From f()");
        }
        public static void main(String[] args) {
            try {
                f();
            } catch(Exception1 e) {
                System.err.println("Caught Exception1");
                e.printStackTrace();
            } finally {
                System.out.println("Made it to finally");
            }
    
        }
    }
    =============================================================
        Throwing MyException from f()
    Exception1(String msg)
    Made it to finally
    Caught Exception1
    第十二章异常处理.Exception1: From f()
    	at 第十二章异常处理.No1Ex.f(No1Ex.java:18)
    	at 第十二章异常处理.No1Ex.main(No1Ex.java:22)
    
    

    练习2

    /* Define an object reference and initialize it to null. Try to call a method
     * through this reference. Now wrap the code in a try-catch clause to catch the
     * exception.
     */
    
    public class No2Ex {
        private static Integer i = null;
        public static void main(String[] args) {
            // leads to NullPointerException:
            // System.out.println(i.toString());
            try {
                System.out.println(i.toString());
            } catch(NullPointerException e) {
                System.err.println("Caught NullPointerException");
                e.printStackTrace();
            }
            try {
                i = 10;
                System.out.println(i.toString());
            } catch(NullPointerException e) {
                System.err.println("Caught NullPointerException");
                e.printStackTrace();
            } finally {
                System.out.println("Got through it");
            }
        }
    }
    =======================================================================
    Caught NullPointerException
    java.lang.NullPointerException
    	at 第十二章异常处理.No2Ex.main(No2Ex.java:14)
    10
    Got through it
    

    练习3

    package 第十二章异常处理;
    // Write code to generate and catch an ArrayIndexOutOfBoundsException.
    
    public class No3Ex {
        private static int[] ia = new int[2];
        public static void main(String[] args) {
            try {
                ia[2] = 3;
            } catch(ArrayIndexOutOfBoundsException e) {
                System.err.println(
                        "Caught ArrayIndexOutOfBoundsException");
                e.printStackTrace();
            }
        }
    }
    =========================================================================
        Caught ArrayIndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException: 2
    	at 第十二章异常处理.No3Ex.main(No3Ex.java:8)
    

    练习4

    /* Create your own exception class using the extends keyword. Write a
     * constructor for this class that takes a String argument and stores it inside
     * the object with a String reference. Write a method that displays the stored
     * String. Create a try-catch clause to exercise your new exception.
     */
    
    class Exception4 extends Exception {
        private String msg;
        Exception4(String msg) {
            super(msg);
            System.out.println("Exception4()");
            this.msg = msg;
        }
        protected void showS() {
            System.out.println("Message from Exception4: " + msg);
        }
    }
    
    public class No4Ex {
        public static void f() throws Exception4 {
            System.out.println("f()");
            throw new Exception4("Ouch from f()");
        }
        public static void main(String[] args) {
            try {
                f();
            } catch(Exception4 e) {
                System.err.println("Caught Exception4");
                e.printStackTrace();
                e.showS();
            }
        }
    }
    =============================================================f()
    Exception4()
    Message from Exception4: Ouch from f()
    Caught Exception4
    第十二章异常处理.Exception4: Ouch from f()
    	at 第十二章异常处理.No4Ex.f(No4Ex.java:24)
    	at 第十二章异常处理.No4Ex.main(No4Ex.java:28)
    
    

    练习5

    /* Create you own resumption-like behavior using a while loop that repeats
     * until an exception is no longer thrown.
     */
    
    public class No5Ex {
        private static int[] ia = new int[2];
        static int x = 5;
        public static void main(String[] args) {
            while(true) {
                try {
                    ia[x] = 1;
                    System.out.println(ia[x]);
                    break;
                } catch(ArrayIndexOutOfBoundsException e) {
                    System.err.println(
                            "Caught ArrayIndexOutOfBoundsException");
                    e.printStackTrace();
                    x--;
                } finally {
                    System.out.println("Are we done yet?");
                }
            }
            System.out.println("Now, we're done.");
        }
    }
    ========================================================================
    Caught ArrayIndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException: 5
    	at 第十二章异常处理.No5Ex.main(No5Ex.java:13)
    Caught ArrayIndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException: 4
    	at 第十二章异常处理.No5Ex.main(No5Ex.java:13)
    Caught ArrayIndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException: 3
    	at 第十二章异常处理.No5Ex.main(No5Ex.java:13)
    Caught ArrayIndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException: 2
    	at 第十二章异常处理.No5Ex.main(No5Ex.java:13)
    Are we done yet?
    Are we done yet?
    Are we done yet?
    Are we done yet?
    1
    Are we done yet?
    Now, we're done.
    

    12.4.1 异常与记录日志

    使用 java.util.logging 工具将输出记录到日志中。

    LoggingException将所有记录日志的基础设施都构建在异常自身中,使得它所使用的方式非常方便。

    练习6

    /* Create two exception classes, each of which performs its own logging
     * automtically. Demonstrate that these work.
     */
    import java.util.logging.*;
    import java.io.*;
    
    class Oops1 extends Exception {
        private static Logger logger = Logger.getLogger("LoggingException");//与错误相关的包名或者类名字
        public Oops1() {
            StringWriter trace = new StringWriter();
            printStackTrace(new PrintWriter(trace));//获取抛出处的栈轨迹,接受Java。io。PrintWriter对象作为参数从而产生字符串
            logger.severe(trace.toString());//severe 直接调用与日志级别相关联的方法
        }
    }
    
    class Oops2 extends Exception {
        private static Logger logger = Logger.getLogger("LoggingException");
        public Oops2() {
            StringWriter trace = new StringWriter();
            printStackTrace(new PrintWriter(trace));
            logger.severe(trace.toString());
        }
    }
    
    public class No6Ex {
        static void f() throws Oops1 {
            throw new Oops1();
        }
        static void g() throws Oops2 {
            throw new Oops2();
        }
        public static void main(String[] args) {
            try {
                f();
            } catch(Exception Oops1) {}
            try {
                g();
            } catch(Exception Oops2) {}
        }
    }
    =======================================================================
    二月 16, 2021 9:44:22 下午 第十二章异常处理.Oops1 <init>
    严重: 第十二章异常处理.Oops1
    	at 第十二章异常处理.No6Ex.f(No6Ex.java:29)
    	at 第十二章异常处理.No6Ex.main(No6Ex.java:36)
    
    二月 16, 2021 9:44:22 下午 第十二章异常处理.Oops2 <init>
    严重: 第十二章异常处理.Oops2
    	at 第十二章异常处理.No6Ex.g(No6Ex.java:32)
    	at 第十二章异常处理.No6Ex.main(No6Ex.java:39)
    
    

    练习7

    // Modify Exercise 3 so that the catch clause logs the result.
    import java.util.logging.*;
    import java.io.*;
    
    public class No7Ex {
        private static int[] ia = new int[2];
        private static Logger logger = Logger.getLogger("Ex7 Exceptions");
        static void logException(Exception e) { // Exception e argument
            StringWriter trace = new StringWriter();
            e.printStackTrace(new PrintWriter(trace));
            logger.severe(trace.toString());
        }
        public static void main(String[] args) {
            try {
                ia[2] = 3;
            } catch(ArrayIndexOutOfBoundsException e) {
                System.err.println(
                        "Caught ArrayIndexOutOfBoundsException");
                e.printStackTrace();
                // call logging method:
                logException(e);
            }
        }
    }
    ====================================================================
        Caught ArrayIndexOutOfBoundsException
    java.lang.ArrayIndexOutOfBoundsException: 2
    	at 第十二章异常处理.No7Ex.main(No7Ex.java:18)
    二月 16, 2021 10:04:45 下午 第十二章异常处理.No7Ex logException
    严重: java.lang.ArrayIndexOutOfBoundsException: 2
    	at 第十二章异常处理.No7Ex.main(No7Ex.java:18)
    

    12.5 异常说明

    Java鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员,它使得调用者能确切知道写什么样的代码可以捕获所有潜在的异常。

    Java使用了一种语法,使你能以礼貌的方式告知客户端程序员某个方法可能会抛出异常 ,然后客户端程序员就可以进行相应的处理。这样异常说明。它属于方法声明的一部分,紧跟在形式参数列表之后。

    练习8

    /* Write a class with a method that throws an exception of the type created
     * in Exercise 4. Try compiling it without an exception specification to see
     * what the compiler says. Add the appropriate exception specification. Try
     * out your class and its exception inside a try-catch clause.
     */
    
    
    class Test8 {
        public static void f() throws Exception4 {
            System.out.println("f()");
            throw new Exception4("Ouch from f()");
        }
    }
    
    public class No8Ex {
        public static void main(String[] args) {
            try {
                Test8 t = new Test8();
                t.f();
            } catch(Exception4 e) {
                System.err.println("Caught Exception4");
                e.printStackTrace();
                e.showS();
            }
        }
    }
    ============================================================
    f()
    Exception4()
    Message from Exception4: Ouch from f()
    Caught Exception4
    第十二章异常处理.Exception4: Ouch from f()
    	at 第十二章异常处理.Test8.f(No8Ex.java:13)
    	at 第十二章异常处理.No8Ex.main(No8Ex.java:21)
    

    12.6 捕获所有异常

    捕获异常的基类Exception

    放在处理程序列表的末尾,以防它抢在其他处理程序之前先把异常捕获了

    Exception没有太多的具体信息,可以调用它的基类Throwable继承方法:

    String getMessage()

    String getLocalizeMessage()

    来获取详细信息,或用本地语言表示详细信息。

    String toString()

    返回对Throwable的简单描述,要是有详细信息的话,也会把它包含在内:

    void printStrackTree() 输出到标准错误

    void printStackTree(PrintStream) 选择PrintStream为输出流

    void printStackTree(java.io.PrintWriter) 选择Jav.io.PrintWriter 为输出流

    打印Throwable和Throwable的调用栈轨迹”把你带到异常抛出点“的方法调用序列

    练习9

    /* Create three new types of exceptions. Write a class with a method that
     * throws all three. In main(), call the method but only use a single catch
     * clause that will catch all three types of exceptions.
     */
    
    class ExceptionA extends Exception {
        ExceptionA(String msg) { super(msg); }
    }
    
    class ExceptionB extends Exception {
        ExceptionB(String msg) { super(msg); }
    }
    
    class ExceptionC extends Exception {
        ExceptionC(String msg) { super(msg); }
    }
    
    public class No9Ex {
        public static void f(int x) throws ExceptionA, ExceptionB, ExceptionC {
            if(x < 0) throw new ExceptionA("x < 0");
            if(x == 0) throw new ExceptionB("x == 0");
            if(x > 0) throw new ExceptionC("x > 0");
        }
        public static void main(String[] args) {
            try {
               // f(0);
                f(1);
                f(-1);
                // will catch any Exception type:
            } catch(Exception e) {
                System.out.println("Caught Exception");
                e.printStackTrace(System.out);
            }
        }
    }
    ====================================================================Caught Exception
    第十二章异常处理.ExceptionC: x > 0
    	at 第十二章异常处理.No9Ex.f(No9Ex.java:24)
    	at 第十二章异常处理.No9Ex.main(No9Ex.java:29)
    

    12.6.1 栈轨迹

    printStrackTrace()方法提供的信息可通过 getStackTree()方法来直接访问,这个方法返回一个由栈轨迹中的元素所构成的数组。其中每一个元素表示栈的一帧,元素0表示栈顶元素,调用序列中的最后一个方法调用,最后一个元素是调用序列中的第一个方法调用。

    12.6.1 重新抛出异常

    重抛异常会把异常抛给上一级环境中的异常处理程序,PrintStackTrace()方法显示的将原来异常抛出点的调用栈西悉尼,而不是重新抛出点的信息。

    使用fillInStackTrace(),返回一个Throwable对象,当前调用栈信息填入原来那个异常对象而建立的,异常新发地,有关原来异常发生点地信息丢失,剩下地是与新的抛出点有关的信息

    不必对异常对象地清理而担心,它们都是new在堆上创建地对象,垃圾回收期会自动把它们清理掉

    12.6.3 异常链

    想在捕获一个异常后抛出另一个异常,并且希望把原始地异常地信息保存下来,被称为异常链

    所有的Throwable都可以接受一个cause对象作为参数,cause用来表示原始异常,这样传递给新异常。

    三种基本异常类提供了带cause的参数构造器:

    • Error java 虚拟机报告系统错误
    • Exception
    • RuntimeException

    如果要把其他类型连接起来,应该使用initCause()方法

    练习10

    /* Create a class with two methods, f() and g(). In g(), throw an exception of
     * a new type that you define. In f(), call g(), catch its exception and, in the
     * catch clause, throw a different exception (of a second type that you define).
     * Test your code in main().
     */
    
    class GException extends Exception {
        GException(String s) { super(s); }
    }
    
    class HException extends Exception {
        HException(String s) { super(s); }
    }
    
    public class No10Ex {
        static void f() {
            try {
                try {
                    g();
                } catch(GException ge) {
                    System.out.println("Caught GException in f inner try");
                    ge.printStackTrace();
                    throw new HException("from f(), inner try");
                }
            } catch(HException he) {
                System.out.println("Caught HException in f() outer try");
                he.printStackTrace(System.out);
            }
        }
        static void g() throws GException {
            throw new GException("from g()");
        }
        public static void main(String[] args) {
            f();
        }
    }
    ======================================================================
        Caught GException in f inner try
    Caught HException in f() outer try
    第十二章异常处理.HException: from f(), inner try
    	at 第十二章异常处理.No10Ex.f(No10Ex.java:25)
    	at 第十二章异常处理.No10Ex.main(No10Ex.java:36)
    第十二章异常处理.GException: from g()
    	at 第十二章异常处理.No10Ex.g(No10Ex.java:33)
    	at 第十二章异常处理.No10Ex.f(No10Ex.java:21)
    	at 第十二章异常处理.No10Ex.main(No10Ex.java:36)
    

    练习11

    // TIJ4 Chapter Exceptions, Exercise 11, page 468
    /* Repeat the previous exercise, but inside the catch clause, wrap g()'s
     * exception in a RuntimeException.
     */
    
    
    public class No11Ex {
        static void f() {
            try {
                g();
            } catch(GException ge) {
                System.out.println("Caught GException in f try");
                ge.printStackTrace();
                throw new RuntimeException(ge);
            }
        }
        static void g() throws GException {
            throw new GException("from g()");
        }
        public static void main(String[] args) {
            f();
        }
    }
    ==============================================================
        Caught GException in f try
    第十二章异常处理.GException: from g()
    	at 第十二章异常处理.No11Ex.g(No11Ex.java:20)
    	at 第十二章异常处理.No11Ex.f(No11Ex.java:12)
    	at 第十二章异常处理.No11Ex.main(No11Ex.java:23)
    Exception in thread "main" java.lang.RuntimeException: 第十二章异常处理.GException: from g()
    	at 第十二章异常处理.No11Ex.f(No11Ex.java:16)
    	at 第十二章异常处理.No11Ex.main(No11Ex.java:23)
    Caused by: 第十二章异常处理.GException: from g()
    	at 第十二章异常处理.No11Ex.g(No11Ex.java:20)
    	at 第十二章异常处理.No11Ex.f(No11Ex.java:12)
    	... 1 more
    
    

    12.7 Java标准异常

    ​ Throwable这个Java类被用来表示任何可以作为异常被抛出的类。

    两种类型:

    • Error用来表示编译时和系统错误
    • Exception 而可以被抛出的基本类型,Java类库,用户方法以及运行时故障都可能抛出Exception型异常。

    12.7.1 特例:RuntimeException

    Java的标准运行时会检查传递给方法的每个引用其是否为null,所以你不必对每个传递给方法的每个引用都检查其是否为null

    运行时异常类型有很多,它们会自动被Java虚拟机抛出,所以不必再异常说明中把它们列出来。这些异常都是RuntimeException继承过来的,”不受检查异常“。这种异常属于错误,将被自动捕获,就不用你亲自动手了。

    只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的。究其原因,RuntimeException代表的是编程错误:

    • 无法预料的错误,你从控制范围之外传递进来的null引用
    • 在代码进行中检查的错误。

    异常被设计

    • 处理一些烦人的运行时错误,代码控制能力之外的因素导致
    • 发现某些编辑器无法检测道德编程错误

    12.8 使用finally进行清理

    ​ 无论try块中的异常是否抛出,他都能得到执行。这通常适用于内存回收之外的情况

    12.8.1 finally用来做什么

    对于没有垃圾回收和 析构函数(当对象不再被使用的时候调用的函数C++)的语言来说,finally非常重要。它是程序员保证:无论try块里发生了什么,内存总能得到释放。Java什么情况下才能用的finally呢?

    当要把除内存资源之外的资源恢复到它们的初始状态时,就要用到finally语句

    • 已经打开的文件或者网络连接
    • 在屏幕上画图形
    • 外部世界的某个开关

    在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高的一层的异常处理程序之前,执行finally语句,当涉及break和continue语句的时候,finally语句也会得到执行。

    练习13

    /* Modify Exercise 9 by adding a finally clause. Verify that your
     * finally clause is executed, even if a NullPointerException is thrown.
     */
    
    
    class ExceptionA extends Exception {
        ExceptionA(String msg) { super(msg); }
    }
    
    class ExceptionB extends Exception {
        ExceptionB(String msg) { super(msg); }
    }
    
    class ExceptionC extends Exception {
        ExceptionC(String msg) { super(msg); }
    }
    
    public class No13Ex {
        // array element will be initialized to null:
        private static Integer[] x = new Integer[1];
        public static void f(int x)
                throws ExceptionA, ExceptionB, ExceptionC {
            if(x < 0) throw new ExceptionA("x < 0");
            if(x == 0) throw new ExceptionB("x == 0");
            if(x > 0) throw new ExceptionC("x > 0");
        }
        public static void main(String[] args) {
            try {
                // to throw NullPointerException:
                f(x[0]);
                f(0);
                f(1);
                f(-1);
                // will catch any Exception type:
            } catch(Exception e) {
                System.out.println("Caught Exception");
                e.printStackTrace(System.out);
            } finally {
                System.out.println("made it to finally");
            }
        }
    }
    =========================================================================
    Caught Exception
    第十二章异常处理.ExceptionB: x == 0
    	at 第十二章异常处理.No13Ex.f(No13Ex.java:26)
    	at 第十二章异常处理.No13Ex.main(No13Ex.java:33)
    made it to finally
    

    练习14

    // Show that the OnOffSwitch.java can fail by throwing a
    // RuntimeException inside the try block.
    
    public class OnOffSwitch14 {
        private static Switch sw = new Switch();
        static Integer[] x = new Integer[1];
        public static void f(int i)
                throws OnOffException1, OnOffException2 {}
        public static void main(String[] args) {
            try {
                sw.on();
                // Code that can throw RuntimeException
                // and leave Switch on:
                f(x[0]);
                sw.off();
            } catch(OnOffException1 e) {
                System.out.println("OnOffException1");
                sw.off();
            } catch(OnOffException2 e) {
                System.out.println("OnOffException2");
                sw.off();
            }
        }
    }
    

    练习15

    // Show that WithFinally.java doesn't fail by throwing a 
    // RuntimeException inside the try block.
    
    public class WithFinally15 {
    	private static Switch sw = new Switch();
    	// set up x[0] = null:
    	private static Integer[] x = new Integer[1];
    	public static void f(int i) 
    	throws OnOffException1, OnOffException2 {}
    	public static void main(String[] args) {
    		try {
    			sw.on();
    			// Code to throw NullPointerException: 
    			f(x[0]);
    		} catch(OnOffException1 e) {
    			System.out.println("OnOffException1");
    		} catch(OnOffException2 e) {
    			System.out.println("OnOffException2");
    		} finally {
    			sw.off();
    		} 
    	}		
    } 
    

    12.8.2 在return中使用finally

    因为finally子句总归是会执行的,所以在一个方法中,可以多个点返回,并且可以保证重要的清理工作仍旧会执行 。

    18.3.3 缺憾:异常丢失

    Java的异常实现也有瑕疵。程序出错的标志,

    用某些特殊的final子句,就会发生这种情况

    • 里层没有catch里层try语句直接final
    • final子句return 即使抛出了异常,不会产生任何输出

    练习18

    // Add a second level of exception loss to LostMessage.java so that the
    // HoHumException is itself replaced by a third exception.
    
    class VeryImportantException extends Exception {
        public String toString() {
            return "A very important exception!";
        }
    }
    
    class HoHumException extends Exception {
        public String toString() {
            return "A trivial exception";
        }
    }
    
    class MeaninglessException extends Exception {
        public String toString() {
            return "A meaningless exception";
        }
    }
    
    public class No18Ex {
        void f() throws VeryImportantException {
            throw new VeryImportantException();
        }
        void dispose() throws HoHumException {
            throw new HoHumException();
        }
        void eliminate() throws MeaninglessException {
            throw new MeaninglessException();
        }
        public static void main(String[] args) {
            try {
                No18Ex lm = new No18Ex();
                try {
                    try {
                        lm.f();
                        lm.dispose();
                    } finally {
                        lm.eliminate();
                    }
                } catch(Exception e) {
                    System.out.println(e);
                }
            } catch(Exception e) {
                System.out.println(e);
            }
        }
    }
    ==========================================================
    A meaningless exception
    

    练习19

    public class LostMessageFound19 {
    	void f() throws VeryImportantException {
    		throw new VeryImportantException();
    	}
    	void dispose() throws HoHumException {
    		throw new HoHumException();
    	}
    	public static void main(String[] args) {
    		try {
    			LostMessageFound19 lmf = new LostMessageFound19();
    			try {
    				lmf.f();
    			} catch(Exception e) {
    				System.out.println(e);
    			} finally {
    				lmf.dispose();
    			}
    
    		} catch(Exception e) {
    			System.out.println(e);
    		}
    	}
    }
    A very important exception!
    A trivial exception
    

    12.9 异常的限制

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

    这意味着当基类使用的代码应用到其派生类对象的时候,一样能够工作(面向对象的基本概念):

    • 方法声明将抛出异常,但实际上没有抛出,这种方式使你能强制用户去捕获可能在覆盖后的event()版本中增加的异常,所以它很合理。这对于抽象方法同样成立。

    • 如果一个类继承了基类和实现了一个接口,那么接口不能向基类中现有的方法增加异常,或改变异常

    • 如果接口里定义的方法不是来自基类,那么方法抛出什么样的异常也都没有问题。或者实现现有方法,但可以不抛出异常

    • 对构造器没有限制,继承类的构造器可以抛出任何异常,而不必理会基类构造所抛出的异常,但是必须包含基类构造器异常

    • 派生类构造器不能捕获基类构造器异常

    • 通过强制派生类遵守基类方法的异常说明,对象的可替换性得到了保证

    • 可以向上转型为基类,编译器就会要求你捕获基类的异常,额能产生更强制的异常类代码

    继承过程中,编译器会对异常进行强制要求,但异常说明本身并不属于方法类型的一部分,不基于异常说明来重载方法。一个出现在基类方法的异常说明的异常,不一定出现在派生类方法的异常说明里。在继承和覆盖的过程中,基类的方法必须出现在派生类里,”某个特定方法的异常说明接口不是变大而是变小的,与接口和类在继承时情形相反 “

    练习20

    /* MOdify StormyInning.java by adding an UmpireArgument exception type
     * and methods that throw this exception. Test the modified hierarchy.
     */
    
    // Overridden methods may throw only the exceptions
    // specified in their base-class versions, or exceptions
    // derived from the base-class exceptions.
    
    import 第十章内部类.No20;
    
    class BaseballException extends Exception {}
    class Foul extends BaseballException {}
    class Strike extends BaseballException {}
    class UmpireArgument extends BaseballException {}
    class ThrownFromGame extends UmpireArgument {}
    
    abstract class Inning {
        public Inning() throws BaseballException {}
        public void event() throws BaseballException {
            // Doesn't actually have to throw anything
        }
        public abstract void atBat() throws Strike, Foul, UmpireArgument;
        public void questionableCall() throws UmpireArgument {}
        public void walk() {} // Throws no checked exceptions
    }
    
    class StormException extends Exception {}
    class RainedOut extends StormException {}
    class PopFoul extends Foul {}
    
    
    interface Storm {
        public void event() throws RainedOut;
        public void rainHard() throws RainedOut;
    }
    
    public class No20StormyInning extends Inning implements Storm {
        // OK to add new exceptions for constructors, but you
        // must deal with the base constructor exceptions:
        public No20StormyInning()
                throws UmpireArgument, RainedOut, BaseballException {}
        public No20StormyInning(String s)
                throws Foul, BaseballException {}
        // Regular methods must comform to base class:
        //! void walk() throws PopFoul {} // Compile error
        // Interface CANNOT add exceptions to existing
        // methods from the base class:
        //! public void event() throws RainedOut {}
        // If method doesn't already exist in the
        // base class, the exception is OK:
        public void rainHard() throws RainedOut {}
        // You can choose to not throw any exceptions,
        // even if the base class version does:
        public void event() {}
        // Overridden methods can throw inherited exceptions:
        public void atBat() throws PopFoul, ThrownFromGame {
            throw new ThrownFromGame();
        }
        public void questionableCall() throws UmpireArgument {
            throw new UmpireArgument();
        }
        public static void main(String[] args) {
            try {
                No20StormyInning si = new No20StormyInning();
                si.atBat();
                si.questionableCall();
            } catch(PopFoul e) {
                System.out.println("Pop foul");
            } catch(UmpireArgument e) {
                System.out.println("Umpire argument (StormyInning20)");
                // } catch(ThrownFromGame e) {
                // System.out.println("Thrown from game");
            } catch(RainedOut e) {
                System.out.println("Rained out");
            } catch(BaseballException e) {
                System.out.println("Generic baseball exception");
            }
            // Strike not thrown in derived version.
            try {
                // What happens if you upcast?
                Inning i = new No20StormyInning();
                i.atBat();
                // You must catch the exceptions from
                // the base-class version of the method:
            } catch(Strike e) {
                System.out.println("Strike");
            } catch(Foul e) {
                System.out.println("Foul");
            } catch(ThrownFromGame e) {
                System.out.println("Thrown from game (Inning)");
            } catch(RainedOut e) {
                System.out.println("Rained out");
            } catch(BaseballException e) {
                System.out.println("Generic baseball exception");
            }
        }
    }
    ========================================================================
     Umpire argument (StormyInning20)
    Thrown from game (Inning)
    

    12.10 构造器

    如果异常发生了,所有的东西能被正确的清理吗?大多数情况时非常安全的,但涉及构造器时,问题就出现了,构造器会把对象设置成安全的初始状态。编写构造器时要格外小心。如果构造器抛出异常,就不能很好的初始化,导致清理行为也可能不能正常工作。

    处理那些具有可失败的构造器,且需要清理的对象。为了正确处理这种情况,对于每个构造器,都必须包含在其自己的try-finally语句中,并且每个对象构造必须都跟随一个try-fianlly语句块以确保清理。

    练习21

    // Demonstrate that a derived-class constructor cannot catch exceptions thrown
    // by its base-class constructor.
    
    class BaseException extends Exception {}
    
    class Base {
        Base() throws BaseException {
            throw new BaseException();
        }
    }
    
    class Derived extends Base {
        // BaseException must be caught (no way) or
        // declared to be thrown:
        Derived() throws BaseException {
           super();
            // not this way, 'catch' without 'try' not allowed:
            // catch(BaseException e) {}
            // not this way either, because call to super
            // must be first statement in constructor:
            // try {
            // super();
            // } catch(BaseException e) {}
        }
    }
    
    public class No21Derived {
        public static void main(String[] args) {
            try {
                Derived d = new Derived();
            } catch(BaseException e) {
                System.out.println("BaseException caught in main()");
            }
    
        }
    }
    ========================================================================
    BaseException caught in main()
    

    练习22

    /* Create a class called FailingConstructor with a constructor that might fail
     * partway through the construction process and throw an exception. In main(),
     * write code that properly guards against this failure.
     */
    
    public class No22Ex {
        Integer[] ia = new Integer[2];
        String s;
        No22Ex(String s) throws Exception {
            ia[0] = 0;
            ia[1] = 1;
            ia[2] = 2;
            this.s = s;
        }
        public static void main(String[] args) {
            try {
                No22Ex fc = new No22Ex("hi");
            } catch(Exception e) {
                System.err.println("Caught Exception in main()");
                e.printStackTrace(System.err);
            } finally {
    
            }
        }
    }
    ===================================================================
        Caught Exception in main()
    java.lang.ArrayIndexOutOfBoundsException: 2
    	at 第十二章异常处理.No22Ex.<init>(No22Ex.java:13)
    	at 第十二章异常处理.No22Ex.main(No22Ex.java:18)
    

    练习23

    /* Add a class with a dispose() method to the previous exercise. Modify
     * FailingConstructor so that the constructor creates one of these disposable
     * objects, after which the constructor might through an exception, after which
     * it creates a second disposable member object. Write code to properly guard
     * against failure, and in main() verify that all possible failure situations
     * are covered.
     */
    // This solution satisfies the conditions called for in FailingConstructor23
    // constructor, which catches its own exceptions.
    
    class Disposable {
        private static int counter = 0;
        private int id = counter++;
        private boolean disposed;
        Disposable() {
            disposed = false;
        }
        void dispose() {
            disposed = true;
        }
        String checkStatus() {
            return (id + " " + (disposed ? "disposed" : "not disposed"));
        }
    }
    
    public class No22FailingConstructor {
        private Integer[] ia = new Integer[2];
        private static Disposable d0;
        private static Disposable d1;
        No22FailingConstructor() throws Exception {
            try {
                d0 = new Disposable();
                try {
                    ia[2] = 2;     // causes exception thrown and
                    // caught in middle try loop
                    try {
                        d1 = new Disposable();
                    } catch(Exception e) {
                        System.out.println("Caught e in inner try loop");
                        e.printStackTrace(System.err);
                        System.out.println("Failed to create d1");
                    }
                } catch(Exception e) {
                    System.out.println("Caught e in middle try loop");
                    e.printStackTrace(System.err);
                    System.out.println("Disposing d0");
                    d0.dispose(); // d0 would have been created
                }
            } catch(Exception e) {
                System.out.println("Caught e in outer try loop");
                e.printStackTrace(System.err);
                System.out.println("Failed to create d0");
            }
    
        }
        public static void main(String[] args) {
            try {
                // the constructor catches its own exceptions:
                No22FailingConstructor fc = new No22FailingConstructor();
            } catch(Exception e) {
                System.err.println("Caught Exception in main()");
                e.printStackTrace(System.err);
            }
        }
    }
    =========================================================
    Caught e in middle try loop
    Disposing d0
    java.lang.ArrayIndexOutOfBoundsException: 2
    	at 第十二章异常处理.No22FailingConstructor.<init>(No22FailingConstructor.java:36)
    	at 第十二章异常处理.No22FailingConstructor.main(No22FailingConstructor.java:61)
    

    练习24

    /* Add a dipsose() method to the FailingConstructor class and write code to properly use
     * this class.
     */
    // Solution modeled from examples in text:
    import java.io.*;
    
    public class No24Ex {
        private BufferedReader in;
        public No24Ex(String fname) throws Exception {
            try {
                in = new BufferedReader(new FileReader(fname));
            } catch(FileNotFoundException e) {
                System.out.println("Could not find file " + fname);
                throw e;
            } catch(Exception e) {
                try {
                    in.close();
                } catch(IOException e2) {
                    System.out.println("in.close() failed");
                }
                throw e;
            }
        }
        public String getLine() {
            String s;
            try {
                s = in.readLine();
            } catch(IOException e) {
                throw new RuntimeException("readLine() failed");
            }
            return s;
        }
        public void dispose() {
            try {
                in.close();
                System.out.println("dispose() successful");
            } catch(IOException e2) {
                throw new RuntimeException("in.close() failed");
            }
        }
        public static void main(String[] args) {
            try {
                No24Ex fc =
                        new No24Ex("No22Ex.java");
                try {
                    String s;
                    int i = 1;
                    while((s = fc.getLine()) != null) {
                        // code to print to new file:
                        // println(i + " " + s);
                        // i++;
                    }
                } catch(Exception e) {
                    System.out.println("Exception caught in main()");
                    e.printStackTrace(System.err);
                } finally {
                    fc.dispose();
                }
            } catch(Exception e) {
                System.out.println("FailingConstructor22b construction failed");
            }
        }
    }
    ===================================================
        Could not find file No22Ex.java
    FailingConstructor22b construction failed
    

    12.11 异常匹配

    抛出异常的时候,异常处理系统会按照代码书写顺序找出 最近 的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找

    派生类的对象也可以匹配其基类的处理程序

    不能把基类的 catch 子句放在最外面,把派生类的异常放在里面,这样派生类异常永远得不到执行

    /* Create a three-level hierarchy of exceptions. Now create a
     * base-class A with a method that throws an exception at the base
     * of your hierarchy. Inherit B from A and override the method so
     * it throws an exception at level two of your hierarchy. Repeat by
     * inheriting class C from B. In main(), create a C and upcast it
     * to A, then call the method.
     */
    
    class LevelOneException extends Exception {}
    class LevelTwoException extends LevelOneException {}
    class LevelThreeException extends LevelTwoException {}
    
    class A {
        void f() throws LevelOneException {
            throw new LevelOneException();
        }
    }
    
    class B extends A {
        void f() throws LevelTwoException {
            throw new LevelTwoException();
        }
    }
    
    class C extends B {
        void f() throws LevelThreeException {
            throw new LevelThreeException();
        }
    }
    
    public class No25Ex {
        public static void main(String[] args) {
            A a = new C();
            try {
                a.f();
            }  catch(LevelThreeException e3) {
                System.out.println("Caught e3");
            }  catch(LevelTwoException e2) {
                System.out.println("Caught e2");
            }  catch(LevelOneException e1) {
                System.out.println("Caught e1");
            }
        }
    }
    =================================================================
    Caught e3
    

    12.12 其他可选方式

    异常处理系统就像一个活门(trap door),使你能放弃程序的正常执行序列。

    重要原则:只有在你知道如果处理的情况下才捕获异常

    重要目标:错误处理的代码同错误发生的地点相分离

    异常检查会强制你在可能还没准备好处理错误的时候被迫假设catch语句,导致吞噬则害错误(harmful is swallowed)Java是强静态语言, 编译时就做类型检查的语言。

    研究 被检查的异常 及其并发症,采取什么方法来解决问题

    Java是强类型语言(编译时就会做类型检查的语言),要对类型检查持怀疑态度。(所有模型都是错误的,但是都是有用的)

    • 不在于编译器是否会强制程序员去处理错误,而是要有一致的,使用异常来报告错误的模型
    • 不在于什么时候进行检查,而是一定要有类型检查,也就是说必须强制程序使用正确的类型,至于这种强制施加于编译时还是运行时,那到没关系

    通过把异常传递给控制台,就不必在main()里写try catch子句了

    把”被检查的异常“转换为”不坚持的异常“,可以把”被检查的异常“包装进RuntimeException里面。

    把 被检查的异常 这种功能屏蔽

    练习27

    // Modify Exercise 3 to convert the exception to a Runtime Exception.
    
    public class No27Ex {
        private static int[] ia = new int[2];
        public static void main(String[] args) {
            try {
                ia[2] = 3;
            } catch(ArrayIndexOutOfBoundsException e) { // convert to RuntimeException:
                throw new RuntimeException(e);
            }
        }
    }
    

    练习28

    /* Modify Exercise 4 so that the custom exception class inherits from 
    * RuntimeException, and show that the compiler allows you to leave
    * out the try block.
    */
    
    class Exception28 extends RuntimeException {
    	private String msg;
    	Exception28(String msg) {
    		super(msg);
    		System.out.println("Exception28()");
    		this.msg = msg;
    	}
    	protected void showS() { 
    		System.out.println("Message from Exception4: " + msg);
    	}
    }
    
    public class Ex28 {
    	public static void f() throws Exception28 {
    		System.out.println("f()");
    		throw new Exception28("Ouch from f()");
    	}
    	public static void main(String[] args) {
    		f();	
    	}	
    }
    ==============================================================
        f()
    Exception28()
    Exception in thread "main" 第十二章异常处理.Exception28: Ouch from f()
    	at 第十二章异常处理.No28Ex.f(No28Ex.java:23)
    	at 第十二章异常处理.No28Ex.main(No28Ex.java:26)
    

    练习29

    /* Modify all the exception types in StormyInning.java so that they extend
    * RuntimeException, and show that no exception specifications or try blocks
    * are necessary. Remove the '//!' comments and show how the methods can be 
    * compiled without specifications.
    */
    
    class BaseballException extends RuntimeException {}
    class Foul extends RuntimeException {}
    class Strike extends RuntimeException {}
    
    abstract class Inning {
    	public Inning() {}
    	public void event() {}
    	public abstract void atBat();
    	public void walk() {} 
    }
    
    class StormException extends RuntimeException {}
    class RainedOut extends RuntimeException {}
    class PopFoul extends RuntimeException {}
    
    interface Storm {
    	public void event();
    	public void rainHard();
    }
    
    public class StormyInning29 extends Inning implements Storm {
    	public StormyInning29() {}
    	public StormyInning29(String s) {}
    	public void walk() {}
    	public void event() {}
    	public void rainHard() {}
    	public void atBat() {}
    	public static void main(String[] args) {
    		StormyInning29 si = new StormyInning29();
    		si.atBat();
    		// What happens if you upcast?
    		Inning i = new StormyInning29();
    		i.atBat();		
    	}
    }
    

    练习30

    /* Modify Human.java so that the exceptions inherit from
     * RuntimeException. Modify main() so that the technique
     * in TurnOffChecking.java is used to handle the different
     * types of exceptions.
     */
    
    class Annoyance extends RuntimeException {}
    class Sneeze extends Annoyance {}
    
    
    class WrapCheckedExceptions {
        void throwRuntimeException(int type) {
            try {
                switch(type) {
                    case(0):
                        throw new Annoyance();
                    case(1):
                        throw new Sneeze();
                    case(2):
                        throw new RuntimeException("Where am I?");
                    default: return;
                }
            }  catch(Exception e) {
                // Adapt to unchecked:
                throw new RuntimeException(e);
            }
        }
    }
    
    public class No30Ex {
        public static void main(String[] args) {
            WrapCheckedExceptions wce =
                    new WrapCheckedExceptions();
            for(int i = 0; i < 3; i++)
                try {
                    if(i < 3)
                        wce.throwRuntimeException(i);
                    else
                        throw new RuntimeException();
                }  catch(RuntimeException re) {
                    try {
                        throw re.getCause();
                    }  catch(Sneeze e) {
                        System.out.print("Sneeze: " + e);
                    }  catch(Annoyance e) {
                        System.out.println("Annoyance: " + e);
                    }  catch(Throwable e) {
                        System.out.println("Throwable: " + e);
                    }
                }
        }
    }
    =====================================================================
        Annoyance: 第十二章异常处理.Annoyance
    Sneeze: 第十二章异常处理.SneezeThrowable: java.lang.RuntimeException: Where am I?
    

    12.13 异常使用指南

    12.14 总结

    如果不使用异常,那你只能完成很有限的工作。报告功能时异常的精髓所在,恢复可能不占10%

    ,恢复也只是展开异常栈。

  • 相关阅读:
    js操作json与字符串相互转换
    12 个基本的工作流模式简介
    activiti数据库表结构全貌解析
    百度人脸识别结构光活体检测功能
    【Indiegogo众筹】$99高性价比RK3399超小型mini主机
    AIO-3288C Android8.1固件发布
    Firefly支持AI引擎Tengine,性能提升,轻松搭建AI计算框架
    【百度人脸识别开发套件】开放人脸识别APP及SDK,加速二次开发进程
    【高性价比】AIO-3288C四核高性能主板
    Firefly推出百度人脸识别套件,助力AI产品落地
  • 原文地址:https://www.cnblogs.com/AronJudge/p/14415769.html
Copyright © 2011-2022 走看看