📄 tij308.htm
字号:
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Bath b = <font color=#0000ff>new</font> Bath();
System.out.println(b);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Inside Bath()"</font>,
<font color=#004488>"Soap()"</font>,
<font color=#004488>"s1 = Happy"</font>,
<font color=#004488>"s2 = Happy"</font>,
<font color=#004488>"s3 = Joy"</font>,
<font color=#004488>"s4 = Joy"</font>,
<font color=#004488>"i = 47"</font>,
<font color=#004488>"toy = 3.14"</font>,
<font color=#004488>"castille = Constructed"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>Note that in the <b>Bath </b>constructor, a statement is executed before any of the initializations take place. When you don’t initialize at the point of definition, there’s still no guarantee that you’ll perform any initialization before you send a message to an object reference—except for the inevitable run-time exception. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_922" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>When <b>toString( )</b> is called it fills in <b>s4</b> so that all the fields are properly initialized by the time they are used. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_923" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="_Toc305593252"></a><a name="_Toc305628724"></a><a name="_Toc312374015"></a><a name="_Toc375545307"></a><a name="_Toc24775636"></a><a name="Heading5161"></a>Inheritance
syntax<br></h2>
<p><a name="Index495"></a>Inheritance is an integral part of Java (and all OOP languages). It turns out that you’re always doing inheritance when you create a class, because unless you explicitly inherit from some other class, you implicitly inherit from Java’s standard root class <a name="Index496"></a><b>Object</b>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_924" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>The syntax for composition is obvious, but to perform inheritance there’s a distinctly different form. When you inherit, you say “This new class is like that old class.” You state this in code by giving the name of the class as usual, but before the opening brace of the class body, put the keyword <a name="Index497"></a><b>extends</b> followed by the name of the <a name="Index498"></a><a name="Index499"></a><i>base class</i>. When you do this, you automatically get all the fields and methods in the base class. Here’s an example: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_925" title="Send BackTalk Comment">Feedback</a></font><br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c06:Detergent.java</font>
<font color=#009900>// Inheritance syntax & properties.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>class</font> Cleanser {
<font color=#0000ff>protected</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<font color=#0000ff>private</font> String s = <font color=#0000ff>new</font> String(<font color=#004488>"Cleanser"</font>);
<font color=#0000ff>public</font> <font color=#0000ff>void</font> append(String a) { s += a; }
<font color=#0000ff>public</font> <font color=#0000ff>void</font> dilute() { append(<font color=#004488>" dilute()"</font>); }
<font color=#0000ff>public</font> <font color=#0000ff>void</font> apply() { append(<font color=#004488>" apply()"</font>); }
<font color=#0000ff>public</font> <font color=#0000ff>void</font> scrub() { append(<font color=#004488>" scrub()"</font>); }
<font color=#0000ff>public</font> String toString() { <font color=#0000ff>return</font> s; }
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Cleanser x = <font color=#0000ff>new</font> Cleanser();
x.dilute(); x.apply(); x.scrub();
System.out.println(x);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Cleanser dilute() apply() scrub()"</font>
});
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Detergent <font color=#0000ff>extends</font> Cleanser {
<font color=#009900>// Change a method:</font>
<font color=#0000ff>public</font> <font color=#0000ff>void</font> scrub() {
append(<font color=#004488>" Detergent.scrub()"</font>);
<font color=#0000ff>super</font>.scrub(); <font color=#009900>// Call base-class version</font>
}
<font color=#009900>// Add methods to the interface:</font>
<font color=#0000ff>public</font> <font color=#0000ff>void</font> foam() { append(<font color=#004488>" foam()"</font>); }
<font color=#009900>// Test the new class:</font>
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Detergent x = <font color=#0000ff>new</font> Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
System.out.println(x);
System.out.println(<font color=#004488>"Testing base class:"</font>);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Cleanser dilute() apply() "</font> +
<font color=#004488>"Detergent.scrub() scrub() foam()"</font>,
<font color=#004488>"Testing base class:"</font>,
});
Cleanser.main(args);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>This demonstrates a number of features. First, in the <b>Cleanser</b> <b>append( )</b> method, <b>String</b>s are concatenated to <b>s</b> using the <b>+=</b> operator, which is one of the operators (along with ‘<b>+</b>’) that the Java designers “overloaded” to work with <a name="Index500"></a><a name="Index501"></a><a name="Index502"></a><b>String</b>s. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_926" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Second, both <b>Cleanser</b> and <b>Detergent</b> contain a <a name="Index503"></a><b>main( )</b> method. You can create a <b>main( )</b> for each one of your classes, and it’s often recommended to code this way so that your test code is wrapped in with the class. Even if you have a lot of classes in a program, only the <b>main( )</b> for the class invoked on the command line will be called. (As long as <b>main( )</b> is <b>public</b>, it doesn’t matter whether the class that it’s part of is <b>public</b>.) So in this case, when you say <b>java Detergent</b>, <b>Detergent.main( )</b> will be called. But you can also say <b>java Cleanser </b>to invoke <b>Cleanser.main( )</b>, even though <b>Cleanser</b> is not a <b>public</b> class. This technique of putting a <b>main( )</b> in each class allows easy unit testing for each class. And you don’t need to remove the <a name="Index504"></a><a name="Index505"></a><b>main( )</b> when you’re finished testing; you can leave it in for later testing. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_927" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Here, you can see that <b>Detergent.main( )</b> calls <b>Cleanser.main( )</b> explicitly, passing it the same arguments from the command line (however, you could pass it any <b>String</b> array). <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_928" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>It’s important that all of the methods in <b>Cleanser </b>are <b>public</b>. Remember that if you leave off any access specifier, the member defaults to package access, which allows access only to package members. Thus, <i>within this package</i>, anyone could use those methods if there were no access specifier. <b>Detergent</b> would have no trouble, for example. However, if a class from some other package were to inherit from <b>Cleanser</b>, it could access only <b>public </b>members. So to plan for inheritance, as a general rule make all fields <b>private </b>and all methods <b>public</b>.<b> </b>(<b>protected </b>members also allow access by derived classes; you’ll learn about this later.) Of course, in particular cases you must make adjustments, but this is a useful guideline. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_929" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Note that <b>Cleanser</b> has a set of methods in its interface: <b>append( )</b>, <b>dilute( )</b>, <b>apply( )</b>, <b>scrub( )</b>, and <b>toString( )</b>. Because <b>Detergent</b> is <i>derived from</i> <b>Cleanser</b> (via the <a name="Index506"></a><b>extends</b> keyword), it automatically gets all these methods in its interface, even though you don’t see them all explicitly defined in <b>Detergent</b>. You can think of inheritance, then, as reusing the class<i>.</i> <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_930" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>As seen in <b>scrub( )</b>, it’s possible to take a method that’s been defined in the base class and modify it. In this case, you might want to call the method from the base class inside the new version. But inside <b>scrub( )</b>, you cannot simply call <b>scrub( )</b>, since that would produce a recursive call, which isn’t what you want. To solve this problem, Java has the keyword <a name="Index507"></a><b>super</b> that refers to the “superclass” that the current class has been inherited from. Thus the expression <a name="Index508"></a><b>super.scrub( )</b> calls the base-class version of the method <b>scrub( )</b>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_931" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>When inheriting you’re not restricted to using the methods of the base class. You can also add new methods to the derived class exactly the way you put any method in a class: just define it. The method <b>foam( )</b> is an example of this. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_932" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>In <b>Detergent.main( ) </b>you can see that for a <b>Detergent</b> object, you can call all the methods that are available in <b>Cleanser</b> as well as in <b>Detergent </b>(i.e., <b>foam( )</b>). <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_933" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h3>
<a name="_Toc375545308"></a><a name="_Toc24775637"></a><a name="Heading5220"></a>Initializing
the base class<br></h3>
<p><a name="Index509"></a><a name="Index510"></a><a name="Index511"></a>Since there are now two classes involved—the base class and the derived class—instead of just one, it can be a bit confusing to try to imagine the resulting object produced by a derived class. From the outside, it looks like the new class has the same interface as the base class and maybe some additional methods and fields. But inheritance doesn’t just copy the interface of the base class. When you create an object of the derived class, it contains within it a <a name="Index512"></a><a name="Index513"></a><i>subobject</i> of the base class. This subobject is the same as if you had created an object of the base class by itself. It’s just that from the outside, the subobject of the base class is wrapped within the derived-class object. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_934" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p><a name="Index514"></a><a name="Index515"></a>Of course, it’s essential that the base-class subobject be initialized correctly, and there’s only one way to guarantee this: perform the initialization in the constructor by calling the base-class constructor, which has all the appropriate knowledge and privileges to perform the base-class initialization. Java automatically inserts calls to the base-class constructor in the derived-class constructor. The following example shows this working with three levels of inheritance: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_935" title="Send BackTalk Comment">Feedback</a></font><br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c06:Cartoon.java</font>
<font color=#009900>// Constructor calls during inheritance.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>class</font> Art {
Art() {
System.out.println(<font color=#004488>"Art constructor"</font>);
}
}
<font color=#0000ff>class</font> Drawing <font color=#0000ff>extends</font> Art {
Drawing() {
System.out.println(<font color=#004488>"Drawing constructor"</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Cartoon <font color=#0000ff>extends</font> Drawing {
<font color=#0000ff>private</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<font color=#0000ff>public</font> Cartoon() {
System.out.println(<font color=#004488>"Cartoon constructor"</font>);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Cartoon x = <font color=#0000ff>new</font> Cartoon();
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Art constructor"</font>,
<font color=#004488>"Drawing constructor"</font>,
<font color=#004488>"Cartoon constructor"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>You can see that the construction happens from the base “outward,” so the base class is initialized before the derived-class constructors can access it. Even if you don’t create a constructor for <b>Cartoon( )</b>, the compiler will synthesize a default constructor for you that calls the base class constructor. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_936" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h4>
<a name="Index516"></a><a name="Heading5255"></a>Constructors with
arguments</h4>
<p>The preceding example has default constructors; that is, they don’t have any arguments. It’s easy for the compiler to call these because there’s no question about what arguments to pass. If your class doesn’t have default arguments, or if you want to call a base-class constructor that has an argument, you must explicitly write the calls to the base-class constructor using the <a name="Index517"></a><a name="Index518"></a><b>super</b> keyword and the appropriate argument list:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c06:Chess.java</font>
<font color=#009900>// Inheritance, constructors and arguments.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>class</font> Game {
Game(<font color=#0000ff>int</font> i) {
System.out.println(<font color=#004488>"Game constructor"</font>);
}
}
<font color=#0000ff>class</font> BoardGame <font color=#0000ff>extends</font> Game {
BoardGame(<font color=#0000ff>int</font> i) {
<font color=#0000ff>super</font>(i);
System.out.println(<font color=#004488>"BoardGame constructor"</font>);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -