📄 tij0082.html
字号:
<font color="#0000ff">if</font>(args.length != 0 &&
args[0].equals("finalize"))
DoBaseFinalization.flag = <font color="#0000ff">true</font>;
<font color="#0000ff">else</font>
System.out.println("not finalizing bases");
<font color="#0000ff">new</font> Frog(); <font color="#009900">// Instantly becomes garbage</font>
System.out.println("bye!");
<font color="#009900">// Must do this to guarantee that all </font>
<font color="#009900">// finalizers will be called:</font>
System.runFinalizersOnExit(<font color="#0000ff">true</font>);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>DoBaseFinalization</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
simply holds a flag that indicates to each class in the hierarchy whether to
call <A NAME="Index686"></A><A NAME="Index687"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>super.finalize( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
This flag is set based on a command-line argument, so you can view the behavior
with and without base-class finalization.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Each
class in the hierarchy also contains a member object of class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Characteristic</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
You will see that regardless of whether the base class finalizers are called,
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Characteristic</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
member objects are always finalized.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Each
overridden
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>finalize( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">must
have access to at least
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>protected
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">members
since the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>finalize( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">method
in class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>protected</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and the compiler will not allow you to reduce the access during inheritance.
(“Friendly” is less accessible than
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>protected</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.)</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Frog.main( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>DoBaseFinalization
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">flag
is configured and a single
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Frog
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">object
is created. Remember that garbage collection and in particular finalization
might not happen for any particular object so to enforce this,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>System.<A NAME="Index688"></A>runFinalizersOnExit(true)</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
adds the extra overhead to guarantee that finalization takes place. Without
base-class finalization, the output is:
</FONT><P></DIV>
<font color="#990000"><PRE>not finalizing bases
Creating Characteristic is alive
LivingCreature()
Creating Characteristic has heart
Animal()
Creating Characteristic can live in water
Amphibian()
Frog()
bye!
Frog finalize
finalizing Characteristic is alive
finalizing Characteristic has heart
finalizing Characteristic can live in water </PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
can see that, indeed, no finalizers are called for the base classes of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Frog</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
But if you add the “finalize” argument on the command line, you get:
</FONT><P></DIV>
<font color="#990000"><PRE>Creating Characteristic is alive
LivingCreature()
Creating Characteristic has heart
Animal()
Creating Characteristic can live in water
Amphibian()
Frog()
bye!
Frog finalize
Amphibian finalize
Animal finalize
LivingCreature finalize
finalizing Characteristic is alive
finalizing Characteristic has heart
finalizing Characteristic can live in water </PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Although
the order the member objects are finalized is the same order that they are
created, technically the <A NAME="Index689"></A><A NAME="Index690"></A><A NAME="Index691"></A>order
of finalization of objects is unspecified. With base classes, however, you have
control over the order of finalization. The best order to use is the one
that’s shown here, which is the reverse of the order of initialization.
Following the form that’s used in C++ for destructors, you should perform
the derived-class finalization first, then the base-class finalization.
That’s because the derived-class finalization could call some methods in
the base class that require that the base-class components are still alive, so
you must not destroy them prematurely.
</FONT><a name="_Toc312374057"></a><a name="_Toc375545340"></a><a name="_Toc408018556"></a><P></DIV>
<A NAME="Heading234"></A><H3 ALIGN=LEFT>
Behavior
of polymorphic methods
<P>inside
constructors
<P><A NAME="Index692"></A><A NAME="Index693"></A><A NAME="Index694"></A></H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
hierarchy of constructor calls brings up an interesting dilemma. What happens
if you’re inside a constructor and you call a dynamically-bound method of
the object being constructed? Inside an ordinary method you can imagine what
will happen – the dynamically-bound call is resolved at run-time because
the object cannot know whether it belongs to the class the method is in or some
class derived from it. For consistency, you might think this is what should
happen inside constructors.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
is not exactly the case. If you call a dynamically-bound method inside a
constructor, the overridden definition for that method is used. However, the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>effect</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
can be rather unexpected, and can conceal some difficult-to-find bugs.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Conceptually,
the constructor’s job is to bring the object into existence (which is
hardly an ordinary feat). Inside any constructor, the entire object might be
only partially formed – you can know only that the base-class objects
have been initialized, but you cannot know which classes are inherited from
you. A dynamically-bound method call, however, reaches “forward” or
“outward” into the inheritance hierarchy. It calls a method in a
derived class. If you do this inside a constructor, you call a method that
might manipulate members that haven’t been initialized yet – a sure
recipe for disaster.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
can see the problem in the following example:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: PolyConstructors.java</font>
<font color="#009900">// Constructors and polymorphism</font>
<font color="#009900">// don't produce what you might expect.</font>
<font color="#0000ff">abstract</font> <font color="#0000ff">class</font> Glyph {
<font color="#0000ff">abstract</font> <font color="#0000ff">void</font> draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
<font color="#0000ff">class</font> RoundGlyph <font color="#0000ff">extends</font> Glyph {
<font color="#0000ff">int</font> radius = 1;
RoundGlyph(<font color="#0000ff">int</font> r) {
radius = r;
System.out.println(
"RoundGlyph.RoundGlyph(), radius = "
+ radius);
}
<font color="#0000ff">void</font> draw() {
System.out.println(
"RoundGlyph.draw(), radius = " + radius);
}
}
<font color="#0000ff">public</font> <font color="#0000ff">class</font> PolyConstructors {
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
<font color="#0000ff">new</font> RoundGlyph(5);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Glyph</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>abstract</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
so it is designed to be overridden. Indeed, you are forced to override it in
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RoundGlyph</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
But the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Glyph</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
constructor calls this method, and the call ends up in
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RoundGlyph.draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which would seem to be the intent. But look at the output:
</FONT><P></DIV>
<font color="#990000"><PRE>Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5 </PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Glyph</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’s
constructor calls
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
the value of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>radius</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
isn’t even the default initial value 1. It’s zero. This would
probably result in either a dot or nothing at all being drawn on the screen,
and you’d be staring, trying to figure out why the program won’t
work.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
<A NAME="Index695"></A><A NAME="Index696"></A>order
of initialization described in the previous section isn’t quite complete,
and that’s the key to solving the mystery. The actual process of
initialization is:
</FONT><P></DIV>
<OL>
<LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> The
storage allocated for the object is initialized to binary zero before anything
else happens.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> The
base-class constructors are called as described previously. At this point, the
overridden
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method is called, (yes,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>before
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RoundGlyph
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">constructor
is called), which discovers a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>radius</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
value of zero, due to step 1.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> Member
initializers are called in the order of declaration.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> The
body of the derived-class constructor is called.
</FONT></OL><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">There’s
an upside to this, which is that everything is at least initialized to zero (or
whatever zero means for that particular data type) and not just left as
garbage. This includes object handles that are embedded inside a class via
composition. So if you forget to initialize that handle you’ll get an
exception at run time. Everything else gets zero, which is usually a telltale
value when looking at output.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">On
the other hand, you should be pretty horrified at the outcome of this program.
You’ve done a perfectly logical thing and yet the behavior is
mysteriously wrong, with no complaints from the compiler. (C++ produces more
rational behavior in this situation.) Bugs like this could easily be buried and
take a long time to discover.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">As
a result, a good guideline for constructors is, “Do as little as possible
to set the object into a good state, and if you can possibly avoid it,
don’t call any methods.” The only safe methods to call inside a
constructor are those that are
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>final</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
in the base class. (This also applies to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>private</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
methods, which are automatically
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>final</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.)
These cannot be overridden and thus cannot produce this kind of surprise.
</FONT><a name="_Toc375545341"></a><a name="_Toc408018557"></a><P></DIV>
<div align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0081.html">Prev</a> | <a href="tij0083.html">Next</a>
</div>
</body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -