《Java编程思想》笔记12-通过异常处理错误

  1. 异常处理的两种基本模型
  2. 异常说明(throws)
  3. 栈轨迹
  4. 异常链
  5. 缺陷:异常丢失
  6. 构造器

异常处理的两种基本模型

  1. 终止模型,Java 和 C++ 支持。假设错误非常关键,程序无法返回到异常发生的地方继续执行。
  2. 恢复模型,异常处理程序的工作是修正错误,然后重新调用出问题的方法,并认为第二次能成功。
    如果 Java 想要实现类似恢复的行为,可以在遇见错误时不要抛出,而是调用方法来修正该错误。或者把 try 块放在 while 循环里,这样可以不断的进入 try 块中,直到得到满意的结果。

异常说明(throws)

可以声明方法抛出异常,实际并不抛出。好处是为异常占个位置,以后就可以抛出这种异常而不用修改已有代码。在定义抽象基类和接口时这种能力很重要,这样子类或接口实现就可以抛出这些预先声明的异常

栈轨迹

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

fillInStackTrace() 重新抛出异常,更新调用栈信息。

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; // 把当前对象重新抛出,printStackTrace() 方法显示的将是原来异常抛出点的调用栈信息,而非重新抛出点的信息。
    }
  }
  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);
      for(StackTraceElement ste : e.getStackTrace()) // 栈轨迹
          System.out.println("栈轨迹:"+ste);
    }
    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 exceptions.Rethrowing.f(Rethrowing.java:8)
    at exceptions.Rethrowing.g(Rethrowing.java:12)
    at exceptions.Rethrowing.main(Rethrowing.java:30)
main: printStackTrace()
java.lang.Exception: thrown from f()
    at exceptions.Rethrowing.f(Rethrowing.java:8)
    at exceptions.Rethrowing.g(Rethrowing.java:12)
    at exceptions.Rethrowing.main(Rethrowing.java:30)
栈轨迹:exceptions.Rethrowing.f(Rethrowing.java:8)
栈轨迹:exceptions.Rethrowing.g(Rethrowing.java:12)
栈轨迹:exceptions.Rethrowing.main(Rethrowing.java:30)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()
    at exceptions.Rethrowing.f(Rethrowing.java:8)
    at exceptions.Rethrowing.h(Rethrowing.java:21)
    at exceptions.Rethrowing.main(Rethrowing.java:38)
main: printStackTrace()
java.lang.Exception: thrown from f()
    at exceptions.Rethrowing.h(Rethrowing.java:25)
    at exceptions.Rethrowing.main(Rethrowing.java:38)

*///:~

异常链

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

所有 Throwable 的子类在构造器中都可以接受一个 cause 对象作为参数。这个 cause 就用来表示原始异常,这样通过把原始异常传递给新的异常,就使得即使在当前位置创建并抛出新的异常,也能通过这个异常链追踪到异常最初发生的位置。在 Throwable 的子类中,只有三种基本的异常类提供了带 cause 参数的构造器。它们是 Error,Exception 和 RuntimeException。如果要把其他类型的异常链接起来,应该使用 initCause() 方法而不是构造器。

class DynamicFieldsException extends Exception {}

public class DynamicFields {

  public void setField(String id, Object value) throws DynamicFieldsException {
    if(value == null) {
      DynamicFieldsException dfe = new DynamicFieldsException();
      dfe.initCause(new NullPointerException());
      throw dfe;
    }
  }
  public static void main(String[] args) {
    DynamicFields df = new DynamicFields();
    try {
      df.setField("number3", null);
    } catch(DynamicFieldsException e) {
      e.printStackTrace(System.out);
    }
  }
} /* Output:
exceptions.DynamicFieldsException
    at exceptions.DynamicFields.setField(DynamicFields.java:18)
    at exceptions.DynamicFields.main(DynamicFields.java:27)
Caused by: java.lang.NullPointerException
    at exceptions.DynamicFields.setField(DynamicFields.java:19)
    ... 1 more
*///:~

缺陷:异常丢失

class VeryImportantException extends Exception {
  public String toString() {
    return "A very important exception!";
  }
}

class HoHumException extends Exception {
  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(); // 丢失异常 VeryImportantException
      } finally {
//      try {
          lm.dispose(); // 把这句也加上 try 块,就不会丢失了
//      }catch(Exception e) {
//        System.out.println(e);
//      }
      }
    } catch(Exception e) {
      System.out.println(e);
    }
  }
} /* Output:
A trivial exception
*///:~

另一种丢失方式,从 finally 字句中返回。

// 没有任何输出
public class ExceptionSilencer {
  public static void main(String[] args) {
    try {
      throw new RuntimeException();
    } finally {
      return;
    }
  }
}

构造器

子类的构造器可以抛出任何异常,但 throws 必须包含父类构造器的异常说明。且不能捕获父类构造器抛出的异常(有隐式 super(),super()必须在子类构造器的第一行,所以无法捕获)。



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com

文章标题:《Java编程思想》笔记12-通过异常处理错误

文章字数:1.1k

本文作者:Bin

发布时间:2018-03-28, 21:18:45

最后更新:2019-08-06, 00:46:42

原始链接:http://coolview.github.io/2018/03/28/Java%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B012-%E9%80%9A%E8%BF%87%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录