📄 chapter12.html
字号:
} <font color=#0000ff>catch</font> (CloneNotSupportedException e) {
System.out.println(<font color=#004488>"MyObject can't clone"</font>);
}
<font color=#0000ff>return</font> o;
}
<font color=#0000ff>public</font> String toString() {
<font color=#0000ff>return</font> Integer.toString(i);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> LocalCopy {
<font color=#0000ff>static</font> MyObject g(MyObject v) {
<font color=#009900>// Passing a handle, modifies outside object:</font>
v.i++;
<font color=#0000ff>return</font> v;
}
<font color=#0000ff>static</font> MyObject f(MyObject v) {
v = (MyObject)v.clone(); <font color=#009900>// Local copy</font>
v.i++;
<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>// Testing handle equivalence,</font>
<font color=#009900>// not object equivalence:</font>
<font color=#0000ff>if</font>(a == b)
System.out.println(<font color=#004488>"a == b"</font>);
<font color=#0000ff>else</font>
System.out.println(<font color=#004488>"a != b"</font>);
System.out.println(<font color=#004488>"a = "</font> + a);
System.out.println(<font color=#004488>"b = "</font> + b);
MyObject c = <font color=#0000ff>new</font> MyObject(47);
MyObject d = f(c);
<font color=#0000ff>if</font>(c == d)
System.out.println(<font color=#004488>"c == d"</font>);
<font color=#0000ff>else</font>
System.out.println(<font color=#004488>"c != d"</font>);
System.out.println(<font color=#004488>"c = "</font> + c);
System.out.println(<font color=#004488>"d = "</font> + d);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">First of all, <B>clone( )</B>
must be accessible so 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><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><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="Index1459"></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="Index1460"></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 try-catch block, to catch an exception that
should never happen (because you’ve implemented the <B>Cloneable</B>
interface).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>LocalCopy</B>, the two
methods <B>g( )</B> and <B>f( )</B> demonstrate the difference between
the two approaches for argument passing. <B>g( )</B> shows passing by
reference in which it modifies the outside object and returns a reference to
that outside object, while <B>f( )</B> clones the argument, thereby
decoupling it and leaving the original object alone. It can then proceed to do
whatever it wants, and even to return a handle to this new object without any
ill effects to the original. Notice the somewhat curious-looking
statement:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>v = (MyObject)v.clone();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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 everything that has a
name is actually a handle. So the handle <B>v</B> is used to
<B>clone( )</B> a copy of what it refers to, and this returns a handle 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><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main( )</B>, the
difference between the effects of the two different argument-passing approaches
in the two different methods is tested. The output is:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>a == b
a = 12
b = 12
c != d
c = 47
d = 48</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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="Index1461"></A><A NAME="Index1462"></A><A NAME="Index1463"></A><B>==</B>
and <B>!=</B> operators are simply comparing the contents of the <I>handles</I>.
If the addresses inside the
<A NAME="Index1464"></A><A NAME="Index1465"></A>handles are the same, the
handles are pointing to the same object and are therefore “equal.”
So what the operators are really testing is whether the handles are aliased to
the same
object!</FONT><A NAME="_Toc375545428"></A><A NAME="_Toc408018661"></A><BR></P></DIV>
<A NAME="Heading373"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The effect of Object.clone( )</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">What actually happens when
<A NAME="Index1466"></A><A NAME="Index1467"></A><B>Object.clone( )</B> is
called that makes it so essential to call
<A NAME="Index1468"></A><A NAME="Index1469"></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 precise 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 <A NAME="Index1470"></A>RTTI to determine the actual object
that’s being cloned. This way, the <B>clone( )</B> method can create
the proper amount of storage and do the correct bitcopy for that
type.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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
handles? The following example tests this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Snake.java</font>
<font color=#009900>// Tests cloning to see if destination of</font>
<font color=#009900>// handles are also cloned.</font>
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Snake <font color=#0000ff>implements</font> Cloneable {
<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>
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>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) {}
<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);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>increment( )</B> method
recursively increments each tag so you can see the change, and the
<B>toString( )</B> recursively prints each tag. The output
is:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>s = :a:b:c:d:e
s2 = :a:b:c:d:e
after s.increment, s2 = :a:c:d:e:f</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This means that only the first
segment is duplicated by <B>Object.clone( )</B>, so it does a
<A NAME="Index1471"></A>shallow copy. If you want the whole snake to be
duplicated – a <A NAME="Index1472"></A>deep copy – you must perform
the additional operations inside your overridden
<B>clone( )</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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 handle in your object; otherwise those handles
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><A NAME="_Toc408018662"></A><BR></P></DIV>
<A NAME="Heading374"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Cloning a composed
object<BR><A NAME="Index1473"></A><A NAME="Index1474"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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> handles, 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><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This example shows what you must do
to accomplish a deep copy when dealing with a composed object:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: DeepCopy.java</font>
<font color=#009900>// Cloning a composed object</font>
<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() {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -