黑马程序员_对象克隆

-------android培训java培训、期待与您交流! ----------

当拷贝一个对象时,原始变量与拷贝变量引用同一个对象。也就是说,改变一个变量所引用的对象将会对另一个变量产生影响。

Employee original = new Employee(“JohnPublic”,50000);
Employee copy = original;
copy.raiseSalary(10);    //oops—also changed original


如果创建一个新对象的新copy,它的最初状态与original一样,但以后将各自改变各自的状态,那就需要clone方法:

Employee copy = original.clone();
copy.raiseSalary(10);    //OK—original uncharged

不过,事情并没有这么简单。clone方法是Object类的一个protected方法,也就是说,在用户编写的代码中不能直接调用它。只有Employee类才能够克隆Employee对象。这里看一下Object类实现的clone方法,由于这个类对具体的对象一无所知,所以只能将各个域进行拷贝。如果对象中的所有数据域都属于数值或基本类型,这样拷贝域没有任何问题,但是,如果在对象中包含子对象的引用,拷贝的结果会使两个域引用同一个子对象,因为原始对象与克隆对象共享这部分信息。

默认的克隆操作是浅拷贝,它没有克隆包含在对象中的内部对象。


如果进行浅拷贝会发生什么呢?这要根据具体情况而定。如果原始对象与浅克隆对象共享的子对象是不可变的,将不会产生任何问题。也确实存在这样的情况。例如,子对象属于String类,这样的不允许改变的类,也有可能子对象在其生命周期内不会发生变化,即没有更改它们的方法,也没有创建对它们引用的方法。

      然而,更常见的情况是子对象可变,因此必须重新定义clone方法,以便实现克隆子对象的深拷贝。在列举的示例中,hireDay域属于Date类,这就是一个可变的子对象。

      对于每一个类,都需要做出下列判断:

1)     默认的克隆方法是否满足要求

2)     默认的克隆方法是否能够通过调用可变子对象的clone得到修补

3)     是否不应该使用clone

实际上,选项3是默认的。如果要选择1或2,类必须:

1)     实现Cloneable接口

2)     实现public访问修饰符重新定义的clone方法

PS:在Object类中,clone方法被声明为protected,因此无法直接调用anObjec.clone()。子类只能调用受保护的clone方法克隆它自己。为此,必须重新定义clone方法,并将它声明为public,这样才能够让所有的方法克隆对象。

 

在这里,Cloneable接口的出现域接口的正常使用没有任何关系。尤其是,它没有指定clone方法,这个方法是从Object类继承而来的。接口在这里只是作为一个标记,表明类设计者知道要进行克隆处理。如果一个对象需要克隆,而没有实现Cloneable接口,就会产生一个已检查异常(checked exception)。

 

PS:Cloneable接口是Java提供的几个标记接口(tagging interface)之一(有些程序员称为marker interface)。我们知道,通常使用接口的目的是为了确保类实现一个或几个特定的方法,Comparable接口就是一个示例。而标记接口没有方法,使用它的唯一目的是可以用instanceof进行类型检查。

if(obj instanceof Cloneable) //省略部分代码

建议在自己编写程序时,不要使用这种技术!!!

 

即使clone的默认实现(浅拷贝能够满足需求,也应该实现Cloneable接口,将clone重新定向为public,并调用super.clone()。下面是一个示例:

classEmployee implements Cloneable
{
	//raisevisibility level to public, change return type
	publicEmployee clone() throws CloneNotSupportedException
	{
		return (Employee)super.clone()
	}
	//省略部分代码
}

      PS:在JavaSE 5.0以前的版本中,clone方法的返回类型总是Object类型,而在Java SE 5.0中,允许克隆方法返回指定的类型。

      刚从看到的克隆方法并没有在Object.clone提供的浅拷贝基础上增加人的新功能,而只是将这个方法声明为public。为了实现深拷贝,必须克隆所有可变的实例域。

classEmployee implements Cloneable
{
	publicEmployee clone() throws CloneNotSupportedException
	{
		//call Object.clone()
		Employee cloned =(Employee)super.clone();
		//clone mutable fields
		cloned.hireDay = (Date)hireDay.clone()
		return cloned;
	}
}

      只要在clone中罕有没有实现Cloneable接口的对象,Object类的clone方法就会抛出一个CloneNotSupportedException异常。当然,Employee和Date类都实现了Cloneable接口,因此不会抛出异常。但是编译器不知道这些情况,因此,需要声明异常:

       public Employee clone() throwsCloneNotSupportedException

如果将上面这种形式替换成捕获异常呢?

public Employee clone()
{
	try
	{
		return super.clone();
	}
	catch(CloneNotSupportExceptione){return null;}
	//thie won’t happen,since we areCloneable
}

这种写法比较适用于final类,否则最好还是在这个地方保留throws说明符。如果不支持克隆,子类具有抛出CloneNotSupportedException的选择权。

 

必须谨慎的实现子类的克隆,例如,一旦为Employee类定义了clone方法,任何人都可以利用他克隆Manager对象。Employee的克隆方法能够完成这项重任吗?这取决于Manager中包含了哪些域?在前面列举的示例中,由于bouns是基本类型,所以不会出现任何问题,但Manager类中可能存在一些需要深拷贝的域,或者一些没有实现Cloneable接口的域。没有人能够保证子类实现的clone一定正确。

PS:所有的数组类型均包含一个clone方法,这个方法被设为public,而不是protected。可以利用这个方法创建一个包含所有数据元素拷贝的一个新数组。例如:

intluckyNumbers = {2,3,5,6,11,13};
intcloned = (int[])luckyNumbers.clone();
cloned[5]= 12;  //doesn’t change luckyNumbers[5]


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值