📄 tij319.htm
字号:
v.increment();
<font color=#0000ff>return</font> v;
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> MyObject f(MyObject v) {
v = (MyObject)v.clone(); <font color=#009900>// Local copy</font>
v.increment();
<font color=#0000ff>return</font> v;
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
MyObject a = <font color=#0000ff>new</font> MyObject(11);
MyObject b = g(a);
<font color=#009900>// Reference equivalence, not object equivalence:</font>
System.out.println(<font color=#004488>"a == b: "</font> + (a == b) +
<font color=#004488>"\na = "</font> + a + <font color=#004488>"\nb = "</font> + b);
MyObject c = <font color=#0000ff>new</font> MyObject(47);
MyObject d = f(c);
System.out.println(<font color=#004488>"c == d: "</font> + (c == d) +
<font color=#004488>"\nc = "</font> + c + <font color=#004488>"\nd = "</font> + d);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"a == b: true"</font>,
<font color=#004488>"a = 12"</font>,
<font color=#004488>"b = 12"</font>,
<font color=#004488>"c == d: false"</font>,
<font color=#004488>"c = 47"</font>,
<font color=#004488>"d = 48"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>First of all, for <b>clone( )</b> to be accessible, you must make it <b>public</b>. Second, for the initial part of your <b>clone( )</b> operation, you should call the base-class version of <b>clone( )</b>. The <b>clone( )</b> that’s being called here is the one that’s predefined inside <b>Object</b>, and you can call it because it’s <b>protected</b> and thereby accessible in derived classes. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2720" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p><b>Object.clone( )</b> figures out how big the object is, creates enough memory for a new one, and copies all the bits from the old to the new. This is called a <a name="Index2166"></a><i>bitwise copy,</i> and is typically what you’d expect a <b>clone( )</b> method to do. But before <b>Object.clone( )</b> performs its operations, it first checks to see if a class is <b>Cloneable</b>—that is, whether it implements the <b>Cloneable</b> interface. If it doesn’t, <b>Object.clone( )</b> throws a <a name="Index2167"></a><b>CloneNotSupportedException</b> to indicate that you can’t clone it. Thus, you’ve got to surround your call to <b>super.clone( )</b> with a <b>try</b> block to catch an exception that should never happen (because you’ve implemented the <b>Cloneable</b> interface). <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2721" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>In <b>LocalCopy</b>, the two methods <b>g( )</b> and <b>f( )</b> demonstrate the difference between the two approaches for argument passing. The <b>g( )</b> method shows passing by reference in which it modifies the outside object and returns a reference to that outside object, whereas <b>f( )</b> clones the argument, thereby decoupling it and leaving the original object alone. It can then proceed to do whatever it wants—even return a reference to this new object without any ill effects to the original. Notice the somewhat curious-looking statement: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0102" title="Send BackTalk Comment">Feedback</a></font><br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>v = (MyObject)v.clone();</PRE></FONT></BLOCKQUOTE><p><br></p>
<p>This is where the local copy is created. To prevent confusion by such a statement, remember that this rather strange coding idiom is perfectly feasible in Java because every object identifier is actually a reference. So the reference <b>v</b> is used to <b>clone( )</b> a copy of what it refers to, and this returns a reference to the base type <b>Object</b> (because it’s defined that way in <b>Object.clone( )</b>) that must then be cast to the proper type. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2722" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>In <b>main( )</b>, the difference between the effects of the two different argument-passing approaches is tested. It’s important to notice that the equivalence tests in Java do not look inside the objects being compared to see if their values are the same. The <a name="Index2168"></a><a name="Index2169"></a><a name="Index2170"></a><b>==</b> and <b>!=</b> operators are simply comparing the <i>references</i>. If the addresses inside the references are the same, the references are pointing to the same object and are therefore “equal.” So what the operators are really testing is whether the references are aliased to the same object! <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2723" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h3>
<a name="Index2171"></a><a name="Index2172"></a><a name="_Toc375545428"></a><a name="_Toc24775996"></a><a name="Heading24737"></a>The
effect of <b>Object.clone( )</b></h3>
<p>What actually happens when <a name="Index2173"></a><a name="Index2174"></a><b>Object.clone( )</b> is called that makes it so essential to call <a name="Index2175"></a><a name="Index2176"></a><b>super.clone( )</b> when you override <b>clone( )</b> in your class? The <b>clone( )</b> method in the root class is responsible for creating the correct amount of storage and making the bitwise copy of the bits from the original object into the new object’s storage. That is, it doesn’t just make storage and copy an <b>Object</b>; it actually figures out the size of the <i>real</i> object (not just the base-class object, but the derived object) that’s being copied and duplicates that. Since all this is happening from the code in the <b>clone( )</b> method defined in the root class (that has no idea what’s being inherited from it), you can guess that the process involves RTTI to determine the actual object that’s being cloned. This way, the <a name="Index2177"></a><b>clone( )</b> method can create the proper amount of storage and do the correct bitwise copy for that type. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2724" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Whatever you do, the first part of the cloning process should normally be a call to <b>super.clone( )</b>. This establishes the groundwork for the cloning operation by making an exact duplicate. At this point you can perform other operations necessary to complete the cloning. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2725" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>To know for sure what those other operations are, you need to understand exactly what <b>Object.clone( )</b> buys you. In particular, does it automatically clone the destination of all the references? The following example tests this:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: appendixa:Snake.java</font>
<font color=#009900>// Tests cloning to see if destination</font>
<font color=#009900>// of references are also cloned.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Snake <font color=#0000ff>implements</font> Cloneable {
<font color=#0000ff>private</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<font color=#0000ff>private</font> Snake next;
<font color=#0000ff>private</font> <font color=#0000ff>char</font> c;
<font color=#009900>// Value of i == number of segments</font>
<font color=#0000ff>public</font> Snake(<font color=#0000ff>int</font> i, <font color=#0000ff>char</font> x) {
c = x;
<font color=#0000ff>if</font>(--i > 0)
next = <font color=#0000ff>new</font> Snake(i, (<font color=#0000ff>char</font>)(x + 1));
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> increment() {
c++;
<font color=#0000ff>if</font>(next != <font color=#0000ff>null</font>)
next.increment();
}
<font color=#0000ff>public</font> String toString() {
String s = <font color=#004488>":"</font> + c;
<font color=#0000ff>if</font>(next != <font color=#0000ff>null</font>)
s += next.toString();
<font color=#0000ff>return</font> s;
}
<font color=#0000ff>public</font> Object clone() {
Object o = <font color=#0000ff>null</font>;
<font color=#0000ff>try</font> {
o = <font color=#0000ff>super</font>.clone();
} <font color=#0000ff>catch</font>(CloneNotSupportedException e) {
System.err.println(<font color=#004488>"Snake can't clone"</font>);
}
<font color=#0000ff>return</font> o;
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Snake s = <font color=#0000ff>new</font> Snake(5, 'a');
System.out.println(<font color=#004488>"s = "</font> + s);
Snake s2 = (Snake)s.clone();
System.out.println(<font color=#004488>"s2 = "</font> + s2);
s.increment();
System.out.println(<font color=#004488>"after s.increment, s2 = "</font> + s2);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"s = :a:b:c:d:e"</font>,
<font color=#004488>"s2 = :a:b:c:d:e"</font>,
<font color=#004488>"after s.increment, s2 = :a:c:d:e:f"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>A <b>Snake</b> is made up of a bunch of segments, each of type <b>Snake</b>. Thus, it’s a singly linked list. The segments are created recursively, decrementing the first constructor argument for each segment until zero is reached. To give each segment a unique tag, the second argument, a <b>char</b>, is incremented for each recursive constructor call. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2726" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>The <b>increment( )</b> method recursively increments each tag so you can see the change, and the <b>toString( )</b> recursively prints each tag. From the output, you can see that only the first segment is duplicated by <b>Object.clone( )</b>, therefore it does a shallow copy. If you want the whole snake to be duplicated—a deep copy—you must perform the additional operations inside your overridden <a name="Index2178"></a><a name="Index2179"></a><b>clone( )</b>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2727" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>You’ll typically call <b>super.clone( )</b> in any class derived from a cloneable class to make sure that all of the base-class operations (including <b>Object.clone( )</b>) take place. This is followed by an explicit call to <b>clone( ) </b>for every reference in your object; otherwise those references will be aliased to those of the original object. It’s analogous to the way constructors are called: base-class constructor first, then the next-derived constructor, and so on, to the most-derived constructor. The difference is that <b>clone( )</b> is not a constructor, so there’s nothing to make it happen automatically. You must make sure to do it yourself. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2728" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h3>
<a name="_Toc24775997"></a><a name="Heading24794"></a>Cloning a composed
object<br></h3>
<p><a name="Index2180"></a><a name="Index2181"></a>There’s a problem you’ll encounter when trying to deep copy a composed object. You must assume that the <b>clone( )</b> method in the member objects will in turn perform a deep copy on <i>their</i> references, and so on. This is quite a commitment. It effectively means that for a deep copy to work, you must either control all of the code in all of the classes, or at least have enough knowledge about all of the classes involved in the deep copy to know that they are performing their own deep copy correctly. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]AppendA_2729" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>This example shows what you must do to accomplish a deep copy when dealing with a composed object:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: appendixa:DeepCopy.java</font>
<font color=#009900>// Cloning a composed object.</font>
<font color=#009900>// {Depends: junit.jar}</font>
<font color=#0000ff>import</font> junit.framework.*;
<font color=#0000ff>class</font> DepthReading <font color=#0000ff>implements</font> Cloneable {
<font color=#0000ff>private</font> <font color=#0000ff>double</font> depth;
<font color=#0000ff>public</font> DepthReading(<font color=#0000ff>double</font> depth) { <font color=#0000ff>this</font>.depth = depth; }
<font color=#0000ff>public</font> Object clone() {
Object o = <font color=#0000ff>null</font>;
<font color=#0000ff>try</font> {
o = <font color=#0000ff>super</font>.clone();
} <font color=#0000ff>catch</font>(CloneNotSupportedException e) {
e.printStackTrace();
}
<font color=#0000ff>return</font> o;
}
<font color=#0000ff>public</font> <font color=#0000ff>double</font> getDepth() { <font color=#0000ff>return</font> depth; }
<font color=#0000ff>public</font> <font color=#0000ff>void</font> setDepth(<font color=#0000ff>double</font> depth){ <font color=#0000ff>this</font>.depth = depth; }
<font color=#0000ff>public</font> String toString() { <font color=#0000ff>return</font> String.valueOf(depth);}
}
<font color=#0000ff>class</font> TemperatureReading <font color=#0000ff>implements</font> Cloneable {
<font color=#0000ff>private</font> <font color=#0000ff>long</font> time;
<font color=#0000ff>private</font> <font color=#0000ff>double</font> temperature;
<font color=#0000ff>public</font> TemperatureReading(<font color=#0000ff>double</font> temperature) {
time = System.currentTimeMillis();
<font color=#0000ff>this</font>.temperature = temperature;
}
<font color=#0000ff>public</font> Object clone() {
Object o = <font color=#0000ff>null</font>;
<font color=#0000ff>try</font> {
o = <font color=#0000ff>super</font>.clone();
} <font color=#0000ff>catch</font>(CloneNotSupportedException e) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -