文章标题
来源:互联网 发布:怎么修改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*/
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- Ubuntu 安装和修改Apache2端口
- 查找(一)
- 文章标题
- 文章标题
- python中的文件数据保存
- 文章标题
- SSD6 Exercise0: 函数指针数组的使用实例
- python中的文件读取注意事项
- SpringMVC之类型转换Converter
- 理解差分约束(转)
- HDU 3699(J) ——A hard Aoshu Problem(DFS暴力搜索)
- python爬虫学习第一天
- 第二章(java内存区域与内存溢出异常)
- 面向对象(类与对象———重点!!!