📄 chapter09.html
字号:
MyException2: Originated in g()
at Inheriting2.g(Inheriting2.java:26)
at Inheriting2.main(Inheriting2.java:39)
Throwing MyException2 from h()
MyException2: Originated in h()
at Inheriting2.h(Inheriting2.java:30)
at Inheriting2.main(Inheriting2.java:44)
e.val() = 47</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since an exception is just another
kind of object, you can continue this process of embellishing the power of your
exception classes. Keep in mind, however, that all this dressing up might be
lost on the client programmers using your packages, since they might simply look
for the exception to be thrown and nothing more. (That’s the way most of
the Java library exceptions are used.) If this is the case, it’s possible
to create a new exception type with almost no code at all:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: SimpleException.java</font>
<font color=#0000ff>class</font> SimpleException <font color=#0000ff>extends</font> Exception {
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This relies on the compiler to
create the default constructor (which automatically calls the base-class default
constructor). Of course, in this case you don’t get a
<B>SimpleException(String)</B> constructor, but in practice that isn’t
used
much.</FONT><A NAME="_Toc375545374"></A><A NAME="_Toc408018600"></A><BR></P></DIV>
<A NAME="Heading293"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Exception restrictions<BR><A NAME="Index946"></A><A NAME="Index947"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This example demonstrates the kinds
of restrictions imposed (at compile time) for exceptions:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: StormyInning.java</font>
<font color=#009900>// Overridden methods may throw only the </font>
<font color=#009900>// exceptions specified in their base-class </font>
<font color=#009900>// versions, or exceptions derived from the </font>
<font color=#009900>// base-class exceptions.</font>
<font color=#0000ff>class</font> BaseballException <font color=#0000ff>extends</font> Exception {}
<font color=#0000ff>class</font> Foul <font color=#0000ff>extends</font> BaseballException {}
<font color=#0000ff>class</font> Strike <font color=#0000ff>extends</font> BaseballException {}
<font color=#0000ff>abstract</font> <font color=#0000ff>class</font> Inning {
Inning() <font color=#0000ff>throws</font> BaseballException {}
<font color=#0000ff>void</font> event () <font color=#0000ff>throws</font> BaseballException {
<font color=#009900>// Doesn't actually have to throw anything</font>
}
<font color=#0000ff>abstract</font> <font color=#0000ff>void</font> atBat() <font color=#0000ff>throws</font> Strike, Foul;
<font color=#0000ff>void</font> walk() {} <font color=#009900>// Throws nothing</font>
}
<font color=#0000ff>class</font> StormException <font color=#0000ff>extends</font> Exception {}
<font color=#0000ff>class</font> RainedOut <font color=#0000ff>extends</font> StormException {}
<font color=#0000ff>class</font> PopFoul <font color=#0000ff>extends</font> Foul {}
<font color=#0000ff>interface</font> Storm {
<font color=#0000ff>void</font> event() <font color=#0000ff>throws</font> RainedOut;
<font color=#0000ff>void</font> rainHard() <font color=#0000ff>throws</font> RainedOut;
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> StormyInning <font color=#0000ff>extends</font> Inning
<font color=#0000ff>implements</font> Storm {
<font color=#009900>// OK to add new exceptions for constructors,</font>
<font color=#009900>// but you must deal with the base constructor</font>
<font color=#009900>// exceptions:</font>
StormyInning() <font color=#0000ff>throws</font> RainedOut,
BaseballException {}
StormyInning(String s) <font color=#0000ff>throws</font> Foul,
BaseballException {}
<font color=#009900>// Regular methods must conform to base class:</font>
<font color=#009900>//! void walk() throws PopFoul {} //Compile error</font>
<font color=#009900>// Interface CANNOT add exceptions to existing</font>
<font color=#009900>// methods from the base class:</font>
<font color=#009900>//! public void event() throws RainedOut {}</font>
<font color=#009900>// If the method doesn't already exist in the</font>
<font color=#009900>// base class, the exception is OK:</font>
<font color=#0000ff>public</font> <font color=#0000ff>void</font> rainHard() <font color=#0000ff>throws</font> RainedOut {}
<font color=#009900>// You can choose to not throw any exceptions,</font>
<font color=#009900>// even if base version does:</font>
<font color=#0000ff>public</font> <font color=#0000ff>void</font> event() {}
<font color=#009900>// Overridden methods can throw </font>
<font color=#009900>// inherited exceptions:</font>
<font color=#0000ff>void</font> atBat() <font color=#0000ff>throws</font> PopFoul {}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
<font color=#0000ff>try</font> {
StormyInning si = <font color=#0000ff>new</font> StormyInning();
si.atBat();
} <font color=#0000ff>catch</font>(PopFoul e) {
} <font color=#0000ff>catch</font>(RainedOut e) {
} <font color=#0000ff>catch</font>(BaseballException e) {}
<font color=#009900>// Strike not thrown in derived version.</font>
<font color=#0000ff>try</font> {
<font color=#009900>// What happens if you upcast?</font>
Inning i = <font color=#0000ff>new</font> StormyInning();
i.atBat();
<font color=#009900>// You must catch the exceptions from the</font>
<font color=#009900>// base-class version of the method:</font>
} <font color=#0000ff>catch</font>(Strike e) {
} <font color=#0000ff>catch</font>(Foul e) {
} <font color=#0000ff>catch</font>(RainedOut e) {
} <font color=#0000ff>catch</font>(BaseballException e) {}
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>Inning</B>, you can see that
both the constructor and the <B>event( )</B> 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 you might add in overridden versions of
<B>event( )</B>. The same idea holds for <B>abstract</B> methods, as seen
in <B>atBat( )</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>interface Storm</B> is
interesting because it contains one method (<B>event( )</B>)that is defined
in <B>Inning</B>, and one method that isn’t. Both methods throw a new type
of exception, <B>RainedOut</B>. When <B>StormyInning</B> <B>extends Inning</B>
and <B>implements Storm</B>, you’ll see that the <B>event( )</B>
method in <B>Storm</B> <I>cannot</I> change the exception interface of
<B>event( )</B> in <B>Inning</B>. 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 <B>interface</B> is not
in the base class, such as <B>rainHard( )</B>,<B> </B>then there’s no
problem if it throws exceptions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The restriction on exceptions does
not apply to <A NAME="Index948"></A><A NAME="Index949"></A>constructors. You can
see in <B>StormyInning </B>that a constructor can throw anything it wants,
regardless of what the base-class constructor throws. However, since a
base-class constructor must always be called one way or another (here, the
default constructor is called automatically), the derived-class constructor must
declare any base-class constructor exceptions in its exception
specification.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The reason
<B>StormyInning.walk( )</B> will not compile is that it throws an
exception, while <B>Inning.walk( )</B> does not. If this was allowed, then
you could write code that called <B>Inning.walk( )</B> and that
didn’t have to handle any exceptions, but then when you substituted an
object of a class derived from <B>Inning</B>, exceptions would be thrown so your
code would break. By forcing the derived-class methods to conform to the
exception specifications of the base-class methods, substitutability of objects
is maintained.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The overridden <B>event( )</B>
method shows that a derived-class version of a method may choose to not throw
any exceptions, even if the base-class version does. Again, this is fine since
it doesn’t break any code that is written assuming the base-class version
throws exceptions. Similar logic applies to <B>atBat( )</B>, which throws
<B>PopFoul</B>, an exception that is derived from <B>Foul</B> thrown by the
base-class version of <B>atBat( )</B>. This way, if someone writes code
that works with <B>Inning</B> and calls <B>atBat( )</B>, they must catch
the <B>Foul</B> exception. Since <B>PopFoul</B> is derived from <B>Foul</B>, the
exception handler will also catch <B>PopFoul</B>. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The last point of interest is in
<B>main( )</B>. Here you can see that if you’re dealing with exactly
a <B>StormyInning</B> object, the compiler forces you to catch only the
exceptions that are specific to that class, but if you upcast to the base type
then the compiler (correctly) forces you to catch the exceptions for the base
type. All these constraints produce much more robust exception-handling
code.</FONT><A NAME="fnB40" HREF="#fn40">[40]</A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s useful to realize that
although exception specifications are enforced by the compiler during
inheritance, the exception specifications are not part of the type of a method,
which is comprised of only the method name and argument types. Therefore, you
cannot overload methods based on exception specifications. In addition, because
an exception specification exists in a base-class version of a method
doesn’t mean that it must exist in the derived-class version of the
method, and this is quite different from inheriting the methods (that is, a
method in the base class must also exist in the derived class). Put another way,
the “exception specification interface” for a particular method may
narrow during inheritance and overriding, but it may not widen – this is
precisely the opposite of the rule for the class interface during
inheritance.</FONT><A NAME="_Toc408018601"></A><BR></P></DIV>
<A NAME="Heading294"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Performing cleanup <BR>with finally</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s often some piece of
code that you want to execute whether or not an exception occurs in a <B>try</B>
block. This usually pertains to some operation other than memory recovery (since
that’s taken care of by the garbage collector). To achieve this effect,
you use a <A NAME="Index950"></A><A NAME="Index951"></A><B>finally</B>
clause</FONT><A NAME="fnB41" HREF="#fn41">[41]</A><FONT FACE="Georgia"> at
the end of all the exception handlers. The full picture of an exception-handling
section is thus:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>try</font> {
<font color=#009900>// The guarded region:</font>
<font color=#009900>// Dangerous stuff that might throw A, B, or C </font>
} <font color=#0000ff>catch</font> (A a1) {
<font color=#009900>// Handle A</font>
} <font color=#0000ff>catch</font> (B b1) {
<font color=#009900>// Handle B</font>
} <font color=#0000ff>catch</font> (C c1) {
<font color=#009900>// Handle C</font>
} <font color=#0000ff>finally</font> {
<font color=#009900>// Stuff that happens every time</font>
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To demonstrate that the
<B>finally</B> clause always runs, try this program:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: FinallyWorks.java</font>
<font color=#009900>// The finally clause is always executed</font>
<font color=#0000ff>public</font> <font color=#0000ff>class</font> FinallyWorks {
<font color=#0000ff>static</font> <font color=#0000ff>int</font> count = 0;
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
<font color=#0000ff>while</font>(<font color=#0000ff>true</font>) {
<font color=#0000ff>try</font> {
<font color=#009900>// post-increment is zero first time:</font>
<font color=#0000ff>if</font>(count++ == 0)
<font color=#0000ff>throw</font> <font color=#0000ff>new</font> Exception();
System.out.println(<font color=#004488>"No exception"</font>);
} <font color=#0000ff>catch</font>(Exception e) {
System.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -