文章标题

来源:互联网 发布:怎么修改tomcat的端口 编辑:程序博客网 时间:2024/05/21 12:46

Java编程思想 第12章 通过异常处理错误

标签(空格分隔): JAVA学习


12.1 概念

12.2 基本异常

12. 3 捕获异常

12.4 创建自定义异常

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();    }    public static void main(String[] args) {        try {            f();        } catch (MyException e) {            e.printStackTrace();        }        try {            g();        } catch (MyException e) {            e.printStackTrace(System.out);        }    }}/* Output:com.java.chapter12.MyExceptionThrowing MyException from f()    at com.java.chapter12.FullConstructors.f(FullConstructors.java:14)Throwing MyException from g()    at com.java.chapter12.FullConstructors.main(FullConstructors.java:23)com.java.chapter12.MyException    at com.java.chapter12.FullConstructors.g(FullConstructors.java:18)    at com.java.chapter12.FullConstructors.main(FullConstructors.java:29)*/

printStackTrace()将打印“从方法调用处直到异常抛出处”的方法调用序列,信息将被输出到标准错误流。
printStackTrace(System.out),信息将被发送到System.out,并自动地被捕获和显示在输出中。

12.5 异常说明

12.6 捕获所有异常

catch (Exception e) {}

这将捕获所有异常,所以最好把它放在处理程序列表的末尾,以防它抢在其他处理程序之前先把异常捕获了。

12.6.1 栈轨迹

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将访问一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈的一帧。

package com.java.chapter12;/** * Created by weijie on 17-7-17. */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 {            g();        } 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);        }        try {            h();        } catch (Exception e) {            System.out.println("main: printStackTrace()");            e.printStackTrace(System.out);        }    }}/* Output:originating the exception in f()Inside g(), e.printStackTrace()java.lang.Exception: thrown from f()    at com.java.chapter12.Rethrowing.f(Rethrowing.java:9)    at com.java.chapter12.Rethrowing.g(Rethrowing.java:13)    at com.java.chapter12.Rethrowing.main(Rethrowing.java:32)main: printStackTrace()java.lang.Exception: thrown from f()    at com.java.chapter12.Rethrowing.f(Rethrowing.java:9)    at com.java.chapter12.Rethrowing.g(Rethrowing.java:13)    at com.java.chapter12.Rethrowing.main(Rethrowing.java:32)originating the exception in f()Inside g(), e.printStackTrace()java.lang.Exception: thrown from f()    at com.java.chapter12.Rethrowing.f(Rethrowing.java:9)    at com.java.chapter12.Rethrowing.g(Rethrowing.java:13)    at com.java.chapter12.Rethrowing.h(Rethrowing.java:22)    at com.java.chapter12.Rethrowing.main(Rethrowing.java:38)Inside h(), e.printStackTrace()java.lang.Exception: thrown from f()    at com.java.chapter12.Rethrowing.f(Rethrowing.java:9)    at com.java.chapter12.Rethrowing.g(Rethrowing.java:13)    at com.java.chapter12.Rethrowing.h(Rethrowing.java:22)    at com.java.chapter12.Rethrowing.main(Rethrowing.java:38)main: printStackTrace()java.lang.Exception: thrown from f()    at com.java.chapter12.Rethrowing.h(Rethrowing.java:26)    at com.java.chapter12.Rethrowing.main(Rethrowing.java:38)*/

12.6.3 异常链

常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。
所有Throwable的子类在构造器中都可以接受一个cause对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。

在Throwable的子类中,只有三种基本的异常类提供了带cause参数的构造器。它们是Error、Exception以及RuntimeException。如果要把其他类型的异常链接起来,应该使用initCause()方法而不是构造器。

package com.java.chapter12;/** * Created by weijie on 17-7-20. */class DynamicFieldsException extends Exception {}public class DynamicFields {    private Object[][] fields;    public DynamicFields(int initialSize) {        fields = new Object[initialSize][2];        for (int i = 0; i < initialSize; i++) {            fields[i] = new Object[]{null, null};        }    }    public String toString() {        StringBuilder result = new StringBuilder();        for (Object[] obj : fields) {            result.append(obj[0]);            result.append(": ");            result.append(obj[1]);            result.append("\n");        }        return result.toString();    }    private int hasField(String id) {        for (int i = 0; i < fields.length; i++) {            if (id.equals(fields[i][0])) {                return i;            }        }        return -1;    }    private int getFieldNumber(String id) throws NoSuchFieldException {        int fieldNum = hasField(id);        if (fieldNum == -1) {            throw new NoSuchFieldException();        }        return fieldNum;    }    private int makeField(String id) {        for (int i = 0; i < fields.length; i++) {            if (fields[i][0] == null) {                fields[i][0] = id;                return i;            }        }        // No empty fields. Add one:        Object[][] tmp = new Object[fields.length + 1][2];        for (int i = 0; i < fields.length; i++) {            tmp[i] = fields[i];        }        for (int i = fields.length; i < tmp.length; i++) {            tmp[i] = new Object[]{null, null};        }        fields = tmp;        // Recursive call with expand fields:        return makeField(id);    }    public Object getField(String id) throws NoSuchFieldException {        return fields[getFieldNumber(id)][1];    }    public Object setField(String id, Object value) throws DynamicFieldsException {        if (value == null) {            // Most exceptions don't have a "cause" constructor.            // In these cases you must use initCause(),            // available in all Throwable subclasses.            DynamicFieldsException dfe = new DynamicFieldsException();            dfe.initCause(new NullPointerException());            throw dfe;        }        int fieldNumber = hasField(id);        if (fieldNumber == -1) {            fieldNumber = makeField(id);        }        Object result = null;        try {            result = getField(id); // Get old value        } catch (NoSuchFieldException e) {            // Use constructor that takes "cause":            throw new RuntimeException(e);        }        fields[fieldNumber][1] = value;        return result;    }    public static void main(String[] args) {        DynamicFields df = new DynamicFields(3);        System.out.println(df);        try {            df.setField("d", "A value for d");            df.setField("number", 47);            df.setField("number2", 48);            System.out.println(df);            df.setField("d", "A new value for d");            df.setField("number3", 11);            System.out.println("df: " + df);            System.out.println("df.getField(\"d\") : " + df.getFieldNumber("d"));            Object field = df.setField("d", null); // Exception        } catch (NoSuchFieldException e) {            e.printStackTrace(System.out);        } catch (DynamicFieldsException e) {            e.printStackTrace(System.out);        }    }}/* Output:null: nullnull: nullnull: nulld: A value for dnumber: 47number2: 48df: d: A new value for dnumber: 47number2: 48number3: 11df.getField("d") : 0com.java.chapter12.DynamicFieldsException    at com.java.chapter12.DynamicFields.setField(DynamicFields.java:69)    at com.java.chapter12.DynamicFields.main(DynamicFields.java:100)Caused by: java.lang.NullPointerException    at com.java.chapter12.DynamicFields.setField(DynamicFields.java:70)    ... 1 more*/

12.7 Java标准异常

Throwable这个Java类被用来表示任何可以作为异常被抛出的类。
Throwable对象可分为两种类型:

  • Error用来表示编译时和系统错误(除特殊情况外,一般不用你关心)
  • Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能抛出Exception型异常。

12.8 使用finally进行清理

无论try块中的异常是否抛出,finally子句都能得到执行。

class ThreeException extends Exception {}public class FinallyWorks {    static int count = 0;    public static void main(String[] args) {        while (true) {            try {                // Post-increment is zero first time:                if (count++ == 0) {                    throw new ThreeException();                }                System.out.println("No exception");            } catch (ThreeException e) {                System.out.println("ThreeException");            } finally {                System.out.println("In finally clause");                if (count == 2) {                    break;  // Out of "while"                }            }        }    }}/* Output:ThreeExceptionIn finally clauseNo exceptionIn finally clause*/

从输出中发现,无论异常是否被抛出,finally子句总能被执行。

12.8.1 finally用来做什么

当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部某个开关。

12.8.2 在return中使用finally

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

public class MultipleReturns {    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("End");            return;        } finally {            System.out.println("Performing cleanup");        }    }    public static void main(String[] args) {        for (int i = 1; i <= 2; i++) {            f(i);        }    }}/* Output:Initialization that requires cleanupPoint 1Performing cleanupInitialization that requires cleanupPoint 1Point 2Performing cleanup*/

12.8.3 缺憾:异常丢失

class VeryImportantException extends Exception {    @Override    public String toString() {        return "A very import exception";    }}class HoHumException extends Exception {    @Override    public String toString() {        return "A trivial exception";    }}public class LostMessage {    void f() throws VeryImportantException {        throw new VeryImportantException();    }    void dispose() throws HoHumException {        throw new HoHumException();    }    public static void main(String[] args) {        try {            LostMessage lm = new LostMessage();            try {                lm.f();            } finally {                lm.dispose();            }        } catch (Exception e) {            System.out.println(e);        }    }}/* Output:A trivial exception*/

从输出中可以看到,VeryImportantException不见了,它被finally子句里的HoHumException所取代。

12.9 异常的限制

class BaseballException extends Exception {}class Foul extends BaseballException {}class Strike extends BaseballException {}abstract class Inning {    public Inning() throws BaseballException {}    public void event() throws BaseballException {}    public abstract void atBat() throws Strike, Foul;    public void walk() {}}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 StormyInning extends Inning implements Storm{    // OK to add new exception for constructors, but you    // must deal with the base constructor exceptions:    // 异常限制对构造器不起作用,然而,因为基类构造器必须以这样或那样的方式被调用,派生类构造器的异常说明必须包含基类构造器的异常说明。    // 而且,派生类构造器不能捕获基类构造器抛出的异常    public StormyInning() throws RainedOut, BaseballException {}    public StormyInning(String s) throws Foul, BaseballException {}    // Interface CANNOT add exceptions to existing    // methods from the base class://    public void event() throws RainedOut {}    // If the method doesn't already exist in the    // base class, the exception is OK:    @Override    public void rainHard() throws RainedOut {}    // Regular methods must conform to base class:    // 常规方法抛出异常必须和基类保持一致//    public void walk() throws PopFoul {} // Compile error    // You can choose to not throw any exception,    // even if the base version does:    // 或者不抛出异常,即使基类抛出了异常    public void event() {}    // Overridden methods can throw inherited exceptions:    // 或者抛出继承的异常    public void atBat() throws PopFoul {}    public static void main(String[] args) {        try {            StormyInning si = new StormyInning();            si.atBat();        } catch (PopFoul e) {            System.out.println("Pop Foul");        } catch (RainedOut rainedOut) {            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 StormyInning();            i.atBat();            // You must catch the exception from the            // base-class version of the method:        } catch (Strike strike) {            System.out.println("Strike");        } catch (Foul e) {            System.out.println("Foul");        } catch (BaseballException e) {            e.printStackTrace();        } catch (RainedOut rainedOut) {            rainedOut.printStackTrace();        }    }}

12.10 构造器

对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式是使用嵌套的try子句。
在创建需要清理的对象之后,立即进入一下try-finally语句块。这样,finally子句在构造失败时是不会执行的,而在构造成功时将总是执行。

class NeedsCleanup {    // Constructor can't fail    private static long counter = 1;    private final long id = counter++;    public void dispose() {        System.out.println("NeedsCleanup " + id + " disposed");    }}class ContructionException extends Exception {}class NeedsCleanup2 extends NeedsCleanup {    // Constructor can fail    public NeedsCleanup2() throws ContructionException {}}public class CleanupIdiom {    public static void main(String[] args) {        NeedsCleanup nc1 = new NeedsCleanup();        try {            // ...        } finally {            nc1.dispose();        }        NeedsCleanup nc2 = new NeedsCleanup();        NeedsCleanup nc3 = new NeedsCleanup();        try {            // ...        } finally {            nc3.dispose();            nc2.dispose();        }        // If construction can fail you must guard each one:        try {            NeedsCleanup2 nc4 = new NeedsCleanup2();            try {                NeedsCleanup2 nc5 = new NeedsCleanup2();                try {                    // ...                } finally {                    nc5.dispose();                }            } catch (ContructionException e) {                // nc5 constructor                System.out.println(e);            } finally {                nc4.dispose();            }        } catch (ContructionException e) {            // nc4 constructor            System.out.println(e);        }    }}/* Output:NeedsCleanup 1 disposedNeedsCleanup 3 disposedNeedsCleanup 2 disposedNeedsCleanup 5 disposedNeedsCleanup 4 disposed*/

12.11 异常匹配

抛出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找。
异常处理还可以捕获抛出的异常的基类对象。

class Annoyance extends Exception {}class Sneeze extends Annoyance {}public class Human {    public static void main(String[] args) {        // Catch the exact type:        try {            throw new Sneeze();        } catch (Sneeze s) {            System.out.println("Caught Sneeze");        } catch (Annoyance annoyance) {            System.out.println("Caught Annoyance");        }        // Catch the base type:        try {            throw new Sneeze();        } catch (Annoyance a) {            System.out.println("Caught Annoyance");        }    }}/* Output:Caught SneezeCaught Annoyance*/
原创粉丝点击