Java的“原型”模式 - new vs clone vs class.newInstance

9
在我的项目中,有一些“原型”工厂通过克隆一个最终的私有实例来创建实例。
这些工厂的作者说,与调用“new”运算符相比,这种模式提供了更好的性能。
使用谷歌获取相关信息时,我没有找到任何相关的内容。以下是从一个未知项目的javdoc中找到的小节:
很遗憾,clone() 比调用 new 运算符要慢。然而,它比调用 java.lang.Class.newInstance() 要快得多,并且比自己编写“克隆器”方法要快一些。
对我来说,这看起来像是 Java 1.1 时代的一种旧的最佳实践。有人知道更多关于这个的吗?在现代 JVM 中使用它是一个好习惯吗?
6个回答

24

当然,这种做法已经完全过时了。自那以后,Java虚拟机已经有了巨大的改进。对象创建非常便宜。另一个相关的做法——对象池技术也已经过时,因为现在对象的创建和清理成本更加高效。对于某些情况下,这种做法可能有用(Jon Skeet在这里给出了一些很好的例子),但它绝不能成为这样一个基础框架库的一部分。

我建议找一些新的库和/或新项目来工作;-)

查看这篇文章Java Urban Performance Legends,获取更多见解。


谢谢提供链接,我知道这个设计有些问题,但我需要一些“学术”证明! - Guillaume
1
我只需要缓解答案:这不适用于Android开发:谷歌最佳实践推广克隆/池使用而非对象创建。 - Guillaume
1
是的,Android是用Java编写的,但它并不完全是Java,所以从技术上讲是正确的。更准确地说,对象池对于Sun的HotSpot JVM(以及类似的其他JVM)已不再必要。Dalvik是一种独立的体系结构,而在资源受限的设备上创建对象和进行垃圾回收可能会很昂贵且痛苦,因此请始终遵循目标平台/JVM的最佳实践。(虽然我希望情况不是这样,但现在确实如此。) - Mark Renouf

2

我为Person类创建了一个简单的基准测试。我正在使用最新的OpenJDK 8:

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

并获得了以下结果:

Benchmark             Mode  Cnt     Score       Error   Units

MyBenchmark.viaClone  avgt   10     10.041 ±    0.059   ns/op
MyBenchmark.viaNew    avgt   10      7.617 ±    0.113   ns/op

这个简单的基准测试表明,实例化一个新对象并从源对象设置相应属性所需的时间比克隆它少25%。

2

哎呀,这是我听过的最糟糕的想法之一。

不要做奇怪的事情。即使你已经进行了测量并看到了一些明显的改进(在这种情况下几乎没有机会),在执行之前请仔细考虑很长时间。谁知道它会在下一个JVM中被修复。然后,你就留下了一些奇怪的代码片段,性能更差,难以阅读,并且可能因此出现一些错误。

我的意思是,开发JVM的人不是傻瓜!使用new

我认为你应该摆脱那个奇怪的代码片段。


2
正如其他人所说,这是一种过时的做法。这是一种已经过时的模式,不幸的是,在使用较新的JVM时,它会增加代码的臃肿,而不会提高性能。
我希望我还有代码可以分享,但是一段时间以前,我对这种模式进行了简单的性能测试,与使用“new”运算符相比,我发现使用“new”运算符最慢也至少与这种模式一样快,并且在最好的情况下更快更有效。我的测试可能没有涵盖某些特殊情况,仍然可以使用这种方法,但总的来说,我建议避免使用这种模式。
另一个注意点是,如果这种模式存在于现有的代码库中,我建议您不要太担心。但是,我也不会编写新代码来扩展此模式,除非不这样做会损害代码库的清晰度和一致性,此时您应该评估是否明智地长期重构此代码以将其从项目中移出。通过“聪明”,我指的是,将此代码重构出项目将节省未来开发和调试所需的时间>重构此代码所需的时间。

1

不。

哦,我需要更多的字来回答。所以让我扩展一下,说这样的微观优化是不合适的,除非存在问题,如果有问题,那么您应该能够衡量它是否会使事情变得更好。


1

我的DecimalFormat测试(OpenJDK 8/Windows/JMH 1.19)显示完全相反的结果:

Benchmark               Mode  Cnt     Score     Error  Units
Format.everyTimeCreate  avgt   10  9326.967 ± 199.511  us/op
Format.useClone         avgt   10  5102.397 ±  72.993  us/op
Format.useGlobalWithTL  avgt   10  4605.604 ±  59.000  us/op

看起来回答什么更好的表现并不那么简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接