Error Handling with Exceptions【4】

 [今天單位內事情比較多,所以翻譯的很少,另外我不知道這樣作是否侵犯了什麼利益,如果有的話請立即通知我,我會在第一時間刪除所有內容,感謝]

Even in cases in which the exception is not caught in the current set of catch clauses, finally will be executed before the exception handling mechanism continues its search for a handler at the next higher level:

甚至假如說異常信息沒有被當前的一組異常條件捕獲的話,在異常處理機制在更高層尋找異常處理之前finally也會被執行。

import com.bruceeckel.simpletest.*;

 

class FourException extends Exception {}

 

public class AlwaysFinally {

  private static Test monitor = new Test();

  public static void main(String[] args) {

    System.out.println("Entering first try block");

    try {

      System.out.println("Entering second try block");

      try {

        throw new FourException();

      } finally {

        System.out.println("finally in 2nd try block");

      }

    } catch(FourException e) {

      System.err.println(

        "Caught FourException in 1st try block");

    } finally {

      System.err.println("finally in 1st try block");

    }

    monitor.expect(new String[] {

      "Entering first try block",

      "Entering second try block",

      "finally in 2nd try block",

      "Caught FourException in 1st try block",

      "finally in 1st try block"

    });

  }

}

The finally statement will also be executed in situations in which break and continue statements are involved. Note that, along with the labeled break and labeled continue, finally eliminates the need for a goto statement in Java.

在使用了breakcontinue的語句中finally語句也會被執行,需要說明的是,在Java中有了帶標籤的breakcontinue以及finally就不在需要goto語句了。

Pitfall: the lost exception

Unfortunately, there’s a flaw in Java’s exception implementation. Although exceptions are an indication of a crisis in your program and should never be ignored, it’s possible for an exception to simply be lost. This happens with a particular configuration using a finally clause:

不幸的是在Java的異常中存在一個缺陷,儘管異常是程序中出現錯誤的提示,並且不可忽略,但是這裏有可能會丟失一個異常。在特定的情況中使用了finally後就會發生。

import com.bruceeckel.simpletest.*;

 

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 {

  private static Test monitor = new Test();

  void f() throws VeryImportantException {

    throw new VeryImportantException();

  }

  void dispose() throws HoHumException {

    throw new HoHumException();

  }

  public static void main(String[] args) throws Exception {

    LostMessage lm = new LostMessage();

    try {

      lm.f();

    } finally {

      lm.dispose();

    }

    monitor.expect(new String[] {

      "Exception in thread /"main/" A trivial exception",

      "/tat LostMessage.dispose(LostMessage.java:24)",

      "/tat LostMessage.main(LostMessage.java:31)"

    });  }

} 

You can see that there’s no evidence of the VeryImportantException, which is simply replaced by the HoHumException in the finally clause. This is a rather serious pitfall, since it means that an exception can be completely lost, and in a far more subtle and difficult-to-detect fashion than the preceding example. In contrast, C++ treats the situation in which a second exception is thrown before the first one is handled as a dire programming error. Perhaps a future version of Java will repair this problem (on the other hand, you will typically wrap any method that throws an exception, such as dispose( ), inside a try-catch clause).

你可以看到關於VeryImportException異常一點痕跡都沒有,它在finally中輕而易舉的被HoHumException代替了,這是一個很嚴重的缺陷,因爲這意味着一個異常信息可能會被整個丟掉,而實際上要比上面的例子展示的更加微妙更加的難以發現。相反在C++中對於第一個異常還沒有被捕獲就拋出了第二個異常這種情況視爲嚴重的程序錯誤。相信不就的版本中Java就會修復此問題。換一種說法,你應該將諸如dispose()這樣的方法放到trycatch的子句中。[Version: 6.0.1 GA中測試時編譯期間就提示錯誤:Severity and Description Path   Resource   Location   Creation Time Id Unhandled exception type VeryImportantException,所以應該已經修復了此問題]

Exception restrictions

When you override a method, you can throw only the exceptions that have been specified in the base-class version of the method. This is a useful restriction, since it means that code that works with the base class will automatically work with any object derived from the base class (a fundamental OOP concept, of course), including exceptions.

當你覆寫方法的時候,你只能拋出基類中該方法拋出的異常,這個限制是很有用的,因爲這意味着能夠使用基類對象的代碼肯定也可以是與和這個基類的派生類的對象,這是一個基本的面向對象的原則當然異常也不能例外。

This example demonstrates the kinds of restrictions imposed (at compile time) for exceptions:

下面的例子展示了在異常上面增加限制條件:

//: c09:StormyInning.java

// Overridden methods may throw only the exceptions

// specified in their base-class versions, or exceptions

// derived from the base-class exceptions.

 

class BaseballException extends Exception {}

class Foul extends BaseballException {}

class Strike extends BaseballException {}

 

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;

  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 StormyInning extends Inning implements Storm {

  // OK to add new exceptions for constructors, but you

  // must deal with the base constructor exceptions:

  public StormyInning() 

    throws RainedOut, BaseballException {}

  public StormyInning(String s) 

    throws Foul, BaseballException {}

  // Regular methods must conform 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 the 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 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.err.println("Pop foul");

    } catch(RainedOut e) {

      System.err.println("Rained out");

    } catch(BaseballException e) {

      System.err.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 exceptions from the

      // base-class version of the method:

    } catch(Strike e) {

      System.err.println("Strike");

    } catch(Foul e) {

      System.err.println("Foul");

    } catch(RainedOut e) {

      System.err.println("Rained out");

    } catch(BaseballException e) {

      System.err.println("Generic baseball exception");

    }

  }

} 

In Inning, you can see that both the constructor and the event( ) method say they will throw an exception, but they never do. This is legal because it allows you to force the user to catch any exceptions that might be added in overridden versions of event( ). The same idea holds for abstract methods, as seen in atBat( ).

Inning類中,你可以看到構造方法和event()方法都聲明爲會拋出異常,但是並沒有在實現中這麼作。這是合法的因爲這樣就強制用戶去捕獲這個異常,因爲在event()的覆寫的方法中可能會使用。同理在atBat()中也使用到了這個辦法。

The interface Storm is interesting because it contains one method (event( )) that is defined in Inning, and one method that isn’t. Both methods throw a new type of exception, RainedOut. When StormyInning extends Inning and implements Storm, you’ll see that the event( ) method in Storm cannot change the exception interface of event( ) in Inning. Again, this makes sense because otherwise you’d never know if you were catching the correct thing when working with the base class. Of course, if a method described in an interface is not in the base class, such as rainHard( ), then there’s no problem if it throws exceptions.

Storm這個接口比較有意思,因爲它包含了一個已經在Inning類中定義了的方法event(),另外一個方法不是。兩個方法都拋出了一個新的Exception類型RainedOut。當StormyInning類繼承了Inning並且實現了Storm接口。你可以看到Stormevent()的方法改變不了Inningevent()的異常信息。它的意思就是說當你使用基類工作的時候你就不知道是否捕獲了正確的異常信息。當然如果一個接口中的方法並沒有在基類中聲明,它拋出異常信息是沒有任何問題的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章