克隆是否比构造函数/工厂方法提供了性能改进?

时间:2009-03-19 17:07:38

标签: java optimization clone cloneable

我正在维护一个旧的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
  }
}

尽管有关过早优化的常见警告,这实际上是某种推荐的成语吗?

5 个答案:

答案 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被执行一次。