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.
在使用了break和continue的语句中finally语句也会被执行,需要说明的是,在Java中有了带标签的break和continue以及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()这样的方法放到try-catch的子句中。[在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接口。你可以看到Storm中event()的方法改变不了Inning中event()的异常信息。它的意思就是说当你使用基类工作的时候你就不知道是否捕获了正确的异常信息。当然如果一个接口中的方法并没有在基类中声明,它抛出异常信息是没有任何问题的。