1.
public class Alias2 {
int i;
Alias2(int ii) {
i = ii;
}
static void f(Alias2 handle) {
handle.i++;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Alias2 x = new Alias2(7);
System.out.println("x:"+x.i);
System.out.println("Calling f(x)");
f(x);
System.out.println("x:"+x.i);
}
}
若需要在一个方法调用期间修改一个参数,且不打算修改外部参数,就应在自己的方法内部制作一个副本,从而保护那个参数。
不存在本地对象,只有本地句柄。然而有时仍需将对象当做“本地的”对待,使我们做出的改变只影响一个本地副本,不会对外面的对象造成影响。
2.若需修改一个对象,同时不想改变调用者的对象,就要制作对象的一个副本。只需要简单的使用clone()方法即可。这个方法在基础类Object中定义成"protected"模式。但在希望克隆的任何衍生类中,必须将其覆盖为"public"模式。
3.
import java.util.*;
class Int {
private int i;
public Int(int ii) {
i = ii;
}
public void increment() {
i++;
}
public String toString() {
return Integer.toString(i);
}
}
public class Cloning {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vector v = new Vector();
for(int i = 0; i < 10; i++)
v.addElement(new Int(i));
System.out.println("v: "+v);
Vector v2 = (Vector)v.clone();
for(Enumeration e = v2.elements(); e.hasMoreElements(); ((Int)e.nextElement()).increment());
System.out.println("v: "+v);
}
}
clone()方法产生了一个Object,后者必须立即重新造型为正确类型。这个例子指出Vector的clone()方法不能自动尝试克隆Vector内包含的每个对象——由于别名问题,老的Vector和克隆的Vector都包含了相同的对象。这种情况叫做“简单复制”或者“浅层复制”,因为它只复制了一个对象的“表面”部分。实际对象除包含这个“表面”以外,还包括句柄指向的所有对象。
4.
尽管克隆方法是在所有类基本的Object中定义的,但克隆仍然不会在每个类里自动进行。这似乎有些不可思议,因为基础类方法在衍生类里是肯定能用的。如果想在一个类里使用克隆方法,唯一的办法就是专门添加一些代码,以保证克隆的正常进行。
为避免我们创建的每个类都默认具有克隆能力,clone()方法在基础类Object里得到了“保留”(设为protected)。
5.
import java.util.*;
class MyObject implements Cloneable {
int i;
MyObject(int ii) {
i = ii;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
}catch(CloneNotSupportedException e) {
System.out.println("MyObject can't clone");
}
return o;
}
public String toString() {
return Integer.toString(i);
}
}
public class LocalCopy {
static MyObject g(MyObject v) {
v.i++;
return v;
}
static MyObject f(MyObject v) {
v = (MyObject)v.clone();
v.i++;
return v;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyObject a = new MyObject(11);
MyObject b = g(a);
if(a == b)
System.out.println("a == b");
else
System.out.println("a != b");
System.out.println("a = "+a);
System.out.println("b = "+ b);
MyObject c = new MyObject(47);
MyObject d = f(c);
if(c == d)
System.out.println("c == d");
else
System.out.println("c != d");
System.out.println("c = "+c);
System.out.println("d = "+d);
}
}
不管怎样,clone()必须能够访问,所以必须将其设为public。其次,作为clone()的初期行动,应调用clone()的基础版本。这里调用的clone()是Object内部预先定义好的。之所以能够调用它,是由于它具有protected属性,所以能在衍生的类访问。
Object.clone()会先检查原先的对象有多大,再为新对象腾出足够多的内存,将所有二进制位从原来的对象复制到新的对象。这叫做按位复制,而且按一般的想法,这个工作应该由clone()方法来做。
在Object.clone()正式开始操作前,首先会检查一个类是否实现了Cloneable接口。若未实现,Object.clone()就抛出一个异常,指出我们不能克隆它。
6.
根类中的clone()方法负责建立正确的存储容量,并通过“按位复制”将二进制位从原始对象中复制到新对象的存储空间。 实际需要调查出欲复制之对象的准确大小,然后复制那个对象。这个过程需要RTTI判断克隆的对象的实际大小。采用这种方式,clone()方法便可建立起正确数量的存储空间,并对那个类型进行正确的按位复制。
不管我们要做什么,克隆过程的第一个部分通常都应该是采用super.clone().通过进行一次准确的复制,这样做可为后续的克隆进程建立起一个良好的基础。随后,可采取另一些必要的操作,以完成最终的克隆。
7.
import java.util.*;
class DepthReading implements Cloneable {
private double depth;
public DepthReading(double depth) {
this.depth = depth;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
class TemperatureReading implements Cloneable {
private long time;
private double temperature;
public TemperatureReading(double temperature) {
time = System.currentTimeMillis();
this.temperature = temperature;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
class OceanReading implements Cloneable {
private DepthReading depth;
private TemperatureReading temperature;
public OceanReading(double tdata, double ddata) {
temperature = new TemperatureReading(tdata);
depth = new DepthReading(ddata);
}
public Object clone() {
OceanReading o = null;
try {
o = (OceanReading)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
o.depth = (DepthReading)o.depth.clone();
o.temperature = (TemperatureReading)o.temperature.clone();
return o;
}
}
public class DeepCopy {
public static void main(String[] args) {
// TODO Auto-generated method stub
OceanReading reading = new OceanReading(33.9, 100.5);
OceanReading r = (OceanReading)reading.clone();
}
}
通常可在一个能克隆的类里调用super.clone(),以确保所有基础类行动(包括Object.clone())能够进行。随后是为对象内每个句柄都明确调用一个clone();否则那些句柄会别名变成原始对象的句柄。构建器的调用也大致相同——首先构造基础类,然后是下一个衍生的构建器......以此类推,直到位于最深层的衍生构建器。区别在于clone()并不是构建器,所以没有办法实现自动克隆。为了克隆,必须由自己明确进行。
试图深层复制合成对象时会遇到一个问题。必须假定成员对象中的clone()方法也能依次对自己的句柄进行深层复制,依此类推。
OceanReading是由DepthReading和TemperatureReading对象合成而成的。为了对其进行深层复制,clone()必须同时克隆OceanReading内的句柄。为达到这个目标,super.clone()的结果必须造型成一个OceanReading对象(以便访问depth和temperature句柄).
8.
class Int2 implements Cloneable {
private int i;
public Int2(int ii) {
i = ii;
}
public void increment() {
i++;
}
public String toString() {
return Integer.toString(i);
}
public Object clone() {
Object o = null;
try {
o = super.clone();
}catch(CloneNotSupportedException e) {
System.out.println("Int2 can't clone");
}
return o;
}
}
class Int3 extends Int2 {
private int j;
public Int3(int i) {
super(i);
}
}
public class AddingClone {
public static void main(String[] args) {
// TODO Auto-generated method stub
Int2 x = new Int2(10);
Int2 x2 = (Int2)x.clone();
x2.increment();
System.out.println("x = "+x+", x2 = "+x2);
Int3 x3 = new Int3(7);
x3 = (Int3)x3.clone();
Vector v = new Vector();
for(int i = 0; i < 10; i++) {
v.addElement(new Int2(i));
}
System.out.println("v: "+v);
Vector v2 = (Vector)v.clone();
for(int i = 0; i < v.size(); i++)
v2.setElementAt(((Int2)v2.elementAt(i)).clone(), i);
for(Enumeration e = v2.elements(); e.hasMoreElements();)
((Int2)e.nextElement()).increment();
System.out.println("v: "+v);
System.out.println("v2: "+v2);
}
}
Int3自Int2继承而来,并添加了一个新的基本类型成员int j。将Int2的clone()当做Int3的clone()调用时,它会调用Object.clone(),判断出当前操作的是Int3,并复制Int3内的所有二进制位。只要没有新增需要克隆的句柄,对Object.clone()的一个调用就能完成所有必要的复制——无论clone()是在层次结构多深的一级定义的。
对Vector进行深层复制的先决条件:在克隆了Vector后,必须在其中遍历,并克隆由Vector指向的每个对象。为了对Hashtable进行深层复制,也必须采取类似的处理。
9.
若在一个对象序列化以后再撤销对它的序列化,或者进行装配,那么实际经历的正是一个"克隆"的过程。
10.
public class Stringer {
static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String q = new String("howdy");
System.out.println(q);
String qq = upcase(q);
System.out.println(qq);
System.out.println(q);
}
}
q传递进入upcase()时,它实际是q的句柄的一个副本。该句柄连接的对象实际只在一个统一的物理位置处。句柄四处传递的时候,它的句柄就会得到复制。s只有在upcase()执行期间才会存在。upcase()完成后,本地句柄s便会消失,而upcase返回结果——还是原来那个字串,只是所有字符都变成了大写。当然,它返回的实际是结果的一个句柄。但它返回的句柄最终句柄最终是为一个新对象。
String类的对象被设计成“不可变”,就会发现类中能够修改String的每个方法都实际都创建和返回了一个崭新的String对象,新对象里包含了修改过的信息——原来的String是原封未动的。
由于String对象是不可变的,所以能根据情况对一个特定的String进行多次别名处理。因为它是只读的,所以一个句柄不可能会改变一些会影响其他句柄的东西。
对字符串来说,同志类叫做StringBuffer,