C#(或java)中的clone方法是否在内部使用new操作符?
这实际上是一个面试问题,关于创建类实例的不同方法。新的和反思是众所周知的。但是clone()也会创建对象。但它是否在内部使用new关键字来创建它?这是在c#的上下文中提出的,但也希望了解java。通常,C#(或java)中的clone方法是否在内部使用new操作符?,c#,java,C#,Java,这实际上是一个面试问题,关于创建类实例的不同方法。新的和反思是众所周知的。但是clone()也会创建对象。但它是否在内部使用new关键字来创建它?这是在c#的上下文中提出的,但也希望了解java。通常,Clone实现将使用受保护的MemberwiseClone方法创建基本对象,然后根据需要调整字段(例如,深度复制列表,而不是让两个实例引用同一个) 不过,并没有什么规定他们必须这样做——他们可以调用构造函数来生成对象,然后根据需要设置字段 在内部,MemberwiseClone只是(我想-这一点不
Clone
实现将使用受保护的MemberwiseClone
方法创建基本对象,然后根据需要调整字段(例如,深度复制列表,而不是让两个实例引用同一个)
不过,并没有什么规定他们必须这样做——他们可以调用构造函数来生成对象,然后根据需要设置字段
在内部,
MemberwiseClone
只是(我想-这一点不确定)对象所占用内存的直接转录,并且不以任何方式涉及构造函数。对于Java,答案是“否”。有关算法的文档非常详细:
此方法创建此对象类的新实例,并使用此对象对应字段的内容初始化其所有字段,就像通过赋值一样;字段的内容本身不是克隆的。因此,此方法执行此对象的“浅拷贝”,而不是“深拷贝”操作
您也可以在实践中轻松地检查它。Clone()
在C中是通过实现ICloneable
接口来实现的。你怎么做完全取决于你自己
比如说,
- 不可变对象,如
和String
只返回RuntimeType
(即它们自己)this
- 一些标准的
调用(即Clone
,System.Array
返回System.Delegate
,它执行CLR调用来创建浅层副本。我想这类似于Object.MemberwiseClone()
(它将创建新引用等),但我不相信(尽管没有程序集来确认)它将调用一个构造函数new()
- 其他对象倾向于实现自己的
过程Clone()
System.Version
public object Clone()
{
Version version = new Version();
version._Major = this._Major;
version._Minor = this._Minor;
version._Build = this._Build;
version._Revision = this._Revision;
return version;
}
好的,大家的共识是,因为new涉及构造函数,而clone不涉及调用构造函数(除非我们提出克隆的实现),所以clone在内部不使用new。对于C#很容易测试object.MemberwiseClone在构造函数方面的功能:
class Program
{
class A : ICloneable
{
public int X = 2;
public A()
{
Console.WriteLine("hiya");
X = 1;
}
public object Clone()
{
A a = MemberwiseClone() as A;
return a;
}
}
static void Main(string[] args)
{
A a = new A();
a.X = 3;
A b = a.Clone() as A;
}
}
在构造函数中放置一个断点。上面的程序只调用一次断点。如果考虑到它,基类中的克隆无法调用构造函数:
- 它将要建造什么?它 能够反映并获得最多 派生类型
- 但是如果是那种类型呢 没有公共的无参数 建造师
- 它能调用一个私有函数吗 或受保护的构造函数和 要初始化的对象 没错
MemberWiseClone的默认实现必须依赖于较低级别的类型初始化,而不是构造。替代方案将难以实现,难以理解,并且是难以发现的bug的来源。在Java中,当调用
clone()时
惯例是,返回的对象应该通过调用super.clone()获得
。最终,这将到达的方法。此时,行为由接口决定:如果类实现,则方法返回对象的逐字段副本;否则将抛出。因此clone()
在不调用构造函数的情况下创建对象,得到的是对象的副本
作为一个旁注,Java的
clone
是,Joshua Bloch建议提供一个复制构造函数或复制工厂。这将在有效的Java第二版中讨论。您省略了该段的开头:“类对象的方法clone执行特定的克隆操作”。因此您的引号仅适用于java.lang.Object中的实现。派生类中的重写可以做什么完全取决于它们:它们通常调用super.clone()然后对结果执行其他操作,但没有强制执行的内容,也没有需要的语句。@EJP您是对的,但也有一个约定:按照约定,返回的对象应该通过调用super.clone获得。因此,您描述的内容是可能的,尽管它被认为是“坏java”。