我正在维护一个旧的Java代码库(jvm 1.4),它似乎使用克隆作为对象实例化的替代方案,我猜测它是一种性能优化。这是一个人为的例子:
public class Foo {
private SomeObject obj; // SomeObject implements Cloneable
public Foo() {
obj = new SomeObject();
obj.setField1("abc"); // these fields will have the same value every time
obj.setField2("def");
}
public void doStuff() {
SomeObject newObj = obj.clone(); // clone it instead of using a factory method
// do stuff with newObj
}
}
尽管有关过早优化的常见警告,这实际上是某种推荐的成语吗?
答案 0 :(得分:4)
据推测,他们想要一份副本。也许他们想将它传递给另一个函数,并且不能确定该函数不会改变它。这是一种确保方法doStuff()相对于它被调用的Foo对象的状态是const的方法。
答案 1 :(得分:3)
调用clone()
而不是复制构造函数或工厂方法的一个原因是没有其他选项可用。
与实现复制构造函数或工厂方法执行相同操作相比,实现clone()
执行浅层对象复制(深度复制更复杂)是微不足道的。要实现clone()
,类需要简单地实现Cloneable
接口并覆盖方法clone()
,并调用通常调用super.clone()
的{{1}}。 Object.clone()
将原始对象的每个属性复制到副本的相应属性中,从而创建浅层副本。
虽然实施Object.clone()
很简单,但仍然很容易忘记实施clone()
。因此,使用Cloneable
复制对象的潜在风险是,如果该对象的类忽略实现clone()
而Cloneable
直接或间接调用clone()
,它将抛出Object.clone()
。
在CloneNotSupportedException
界面的code example上查看此discussion和之前的poor design。
答案 2 :(得分:2)
复制构造函数的一个主要问题是必须在编译时知道对象的类型。如果可继承类支持复制构造函数,并且构造函数传递了派生类对象,则构造函数将生成一个基类对象,其基类属性通常与传入对象的属性匹配,但新对象赢了t支持传入对象中存在的基类中不存在的任何功能。
有可能通过使复制构造函数“受保护”,并在每个派生类中都有一个可覆盖的工厂复制方法来解决这个问题,该类调用该类自己的复制构造函数,后者又调用其基类的复制构造函数。但是,每个派生类都需要复制构造函数和复制方法的覆盖,无论它是否添加任何新字段。如果案例类使用“克隆”,则可以消除此额外代码。
答案 3 :(得分:1)
这可能是性能优化,具体取决于构造函数中的工作量。
它更有可能被使用,因为语义不同。克隆提供了一种实现“原型语义”(如javascript,self等)的方法,这种语言通常不会那样。
答案 4 :(得分:0)
如果SomeObject构造函数执行昂贵的工作,例如从数据库中获取某些内容或解析某些内容,或者从文件中读取内容,那么克隆将有助于避免执行该工作。
如果构造函数什么都不做,那么就没有必要使用clone。
编辑:添加代码以显示克隆不必执行与构造函数相同的工作:
class Main
implements Cloneable
{
private final double pi;
public Main()
{
System.out.println("in Main");
// compute pi to 1,000,000,000 decimal palaces
pi = 3.14f;
}
public Object clone()
{
try
{
return (super.clone());
}
catch(final CloneNotSupportedException ex)
{
throw new Error(); // would not throw this in real code
}
}
public String toString()
{
return (Double.toString(pi));
}
public static void main(String[] args)
{
final Main a;
final Main b;
a = new Main();
b = (Main)a.clone();
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
主要的construtor被调用一次,计算pi被执行一次。