简介:Java中的 clone 方法用于创建对象的副本,涉及浅拷贝和深拷贝。本文深入探讨了 clone 的工作原理、实现步骤、使用注意事项和个人实践经验。在实现克隆时,类必须实现 Cloneable 接口并覆盖 Object 类的 clone 方法。正确使用 clone 需要考虑权限、异常处理、对象可变性与不变性等因素。尽管 clone 方法在实现对象副本方面快速,但可能复杂且容易出错,因此在实际开发中常考虑使用 copy-constructor 或 Builder 模式。
1. Java对象克隆概念
1.1 克隆的定义和目的
在Java中,对象克隆是指创建一个具有相同属性值的新对象实例,而不直接使用原有的对象实例。克隆的概念是为了在不违反封装原则的情况下复制对象,允许对象提供一个能够获得其状态副本的方法。
1.2 克隆的意义
对象克隆在很多场景中都非常有用,比如需要在算法中创建对象的副本、在多线程环境下避免竞争条件、或者在进行对象状态回滚的时候。通过实现克隆,可以避免复杂的对象拷贝逻辑,提高代码的可读性和维护性。
public class CloneExample implements Cloneable {
@Override
public Object clone() {
try {
// 通过调用Object类的clone方法实现克隆
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 不应该发生的异常,因为实现了Cloneable接口
}
}
}
在上述示例中,我们展示了如何实现一个简单的克隆, CloneExample 类通过重写 clone() 方法并调用 super.clone() 来实现对象状态的复制。然而,实现克隆并不止步于如此简单的代码,接下来的章节将深入探讨克隆的更多细节和最佳实践。
2. 实现Cloneable接口
2.1 Cloneable接口的作用
2.1.1 认识Cloneable接口
Cloneable 接口在Java中是一个特殊的标记接口,它的存在并不包含任何方法,而其作用是指示一个类的对象可以被克隆。当一个类实现了 Cloneable 接口, Object 类的 clone 方法会返回该对象的一个拷贝。这听起来可能令人困惑,因为通常我们不会实现一个没有任何方法的接口。然而,正是因为 Cloneable 的空实现,它改变了 Object 类中 clone 方法的行为。
2.1.2 如何正确实现Cloneable接口
为了实现 Cloneable 接口,一个类只需要在类定义中包含它。下面是一个简单的示例:
public class MyObject implements Cloneable {
// 类的字段和方法
}
然而,仅仅实现 Cloneable 接口并不足够,因为接口本身不提供任何方法实现。我们需要覆盖 Object 类中的 clone 方法才能进行真正的对象克隆。在覆盖 clone 方法时,我们还需要处理 CloneNotSupportedException ,这是因为 Object 类中的 clone 方法签名中声明了抛出此异常。
2.2 探索Object类的clone方法
2.2.1 clone方法的默认行为
Object 类中定义了 clone 方法,其默认行为是浅拷贝。浅拷贝指的是对象的字段被复制,但这些字段引用的对象不会被复制。换言之,只有对象本身被复制,它引用的内部对象仍然保持不变。 clone 方法返回的是当前对象的一个新实例,其类型为 Object ,因此需要进行类型转换。
public class Object {
protected native Object clone() throws CloneNotSupportedException;
}
2.2.2 为什么需要覆写clone方法
默认的 clone 方法不会满足所有需求,因为它只提供了浅拷贝。如果对象包含引用类型的字段,这些引用的对象并不会被复制,而只是复制了引用。这就意味着在原始对象和克隆对象之间共享了引用对象的状态,这往往不是我们想要的结果。为了实现深拷贝,我们需要覆写 clone 方法,并实现我们自己的对象复制逻辑。
覆写 clone 方法时,我们通常会这样做:
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 由于我们支持克隆,这个异常不应该发生
}
}
这段代码通过调用 super.clone() 来创建对象的一个浅拷贝。如果需要实现深拷贝,我们必须在覆写的方法中添加额外的逻辑来复制对象内部的引用类型字段。
3. clone方法的覆写
3.1 如何覆写clone方法
在Java中,要想实现对象的克隆,必须覆写clone方法。clone方法是Object类提供的一个受保护的方法。当需要实现克隆功能时,必须注意覆写该方法并指定正确的访问修饰符,同时遵守Java的约定,确保对象状态的正确复制。
3.1.1 覆写clone方法的基本步骤
覆写clone方法一般包括以下步骤:
- 在类中声明实现Cloneable接口。
- 在类中显式覆写clone方法。
- 在方法内部调用Object类的clone方法。
- 确保返回一个新的对象实例。
- 调整对象内部状态,处理可变类中的成员变量。
以下是一个简单的示例代码,展示如何覆写clone方法:
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 必须调用super.clone()来创建对象的副本
Person clonedPerson = (Person) super.clone();
// 返回新创建的对象引用
return clonedPerson;
}
// Getter and Setter
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;
}
}
3.1.2 覆写时应遵守的约定
在覆写clone方法时,有几点重要的约定需要遵守,以保证对象克隆的正确性:
- clone方法必须返回Object类型。
- clone方法必须抛出CloneNotSupportedException异常,声明方式为
protected。 - 覆写clone方法应尽可能保持方法的简洁性,不要执行复杂的逻辑。
- 不要忘记调用super.clone(),它会创建对象的浅拷贝。
- 要手动处理对象中所有的可变引用类型成员变量,以实现深拷贝。
3.2 clone方法的访问权限
选择合适的访问权限对于对象克隆的安全性与效率至关重要。clone方法的访问权限决定了对象能否被外部类正确克隆。
3.2.1 选择合适访问修饰符的重要性
在覆写clone方法时,应选择最合适的访问修饰符,通常有以下几种选择:
-
protected: 这是默认访问权限,允许子类和同一个包内的类访问该方法。 -
public: 这种访问权限允许所有其他类调用该方法,但需要慎重考虑其安全性。
访问权限的选择取决于类的使用场景和设计,但需要注意的是,clone方法不建议声明为public,因为它不遵循常规的构造函数模式,并且可能破坏封装性。
3.2.2 访问权限对对象克隆的影响
访问权限的选择会影响克隆操作的可行性:
- 如果将clone方法声明为
public,那么任何类都可以访问并克隆该对象,这在某些情况下可能不是设计者预期的。 - 如果将clone方法声明为
protected或包内可见(不声明访问修饰符),那么只有子类或同一个包内的其他类可以访问clone方法,这为控制对象克隆提供了一定的保护。
以下是一个具有不同访问权限的clone方法声明的示例:
// 使用默认的protected访问修饰符
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 如果需要,也可以显式声明为public
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
在实际应用中,确保选择的访问权限能够满足对象克隆的需求,同时还要考虑到安全性与封装性的平衡。
4. 浅拷贝与深拷贝的探讨
4.1 浅拷贝的定义和影响
4.1.1 浅拷贝的工作原理
浅拷贝(Shallow Copy)是指对象的拷贝过程中,只是复制了对象的基本类型数据和引用类型数据的引用,而不复制引用类型数据所指向的对象本身。在Java中,实现浅拷贝通常利用 Object 类的 clone() 方法,但默认情况下, clone() 方法执行的是浅拷贝。
在浅拷贝的过程中,如果原始对象中的属性包含引用类型,那么新对象中对应的属性仅仅复制了引用地址,因此新旧对象中该属性指向的内存地址是相同的。这意味着,如果通过任一对象修改了引用类型的属性,另一对象的相同属性也会受到影响。
4.1.2 浅拷贝可能带来的问题
由于浅拷贝只复制了对象的引用,并未复制实际的数据,所以在使用时可能会遇到一些问题。最典型的问题是在修改克隆对象的可变属性时,会影响到原始对象。
例如,如果我们有一个包含多个对象引用的数组或集合,然后进行浅拷贝,那么数组或集合中的对象本身并不会被复制。这样,无论是原始对象还是克隆对象,对这些对象的任何修改都会反映到对方上,这就可能违反了克隆对象的初衷。
代码示例:
public class ShallowCopyExample {
public static void main(String[] args) {
OriginalObject original = new OriginalObject();
OriginalObject cloned = (OriginalObject) original.clone();
original.changeReferenceObject(); // 修改引用对象的内容
System.out.println("Original reference value: " + original.getReferenceObject().getValue());
System.out.println("Cloned reference value: " + cloned.getReferenceObject().getValue());
}
}
class OriginalObject implements Cloneable {
private ReferenceObject referenceObject = new ReferenceObject("Initial Value");
public OriginalObject() {}
public void changeReferenceObject() {
referenceObject.setValue("Changed Value");
}
public ReferenceObject getReferenceObject() {
return referenceObject;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class ReferenceObject {
private String value;
public ReferenceObject(String value) {
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
输出结果:
Original reference value: Changed Value
Cloned reference value: Changed Value
在上面的例子中, OriginalObject 包含一个 ReferenceObject 类型的属性。使用 clone() 方法进行浅拷贝后,无论原始对象还是克隆对象修改了 ReferenceObject ,对方的值也会发生变化。因此,浅拷贝并不适用于对象中包含可变引用类型成员变量的场景。
4.2 深拷贝的实现和重要性
4.2.1 深拷贝的实现策略
深拷贝(Deep Copy)是指对象的拷贝过程中,不仅复制对象本身,还递归复制了对象所包含的所有成员变量,确保新对象与原始对象在内存中是完全独立的。深拷贝的实现通常需要在克隆方法中对每个可变成员变量进行递归的复制操作。
实现深拷贝的方法包括:
- 手动实现克隆逻辑 :在 clone() 方法中,手动复制对象中的每个可变成员变量。
- 使用序列化 :将对象序列化到字节流中,然后再反序列化回对象,以获得一个新的独立副本。
- 通过拷贝构造函数 :创建一个新的对象实例,并通过构造函数将旧对象中的每个成员变量复制到新对象中。
代码示例:
public class DeepCopyExample {
public static void main(String[] args) {
OriginalObject original = new OriginalObject();
OriginalObject cloned = original.deepClone();
original.changeReferenceObject(); // 修改原始对象的引用对象内容
System.out.println("Original reference value: " + original.getReferenceObject().getValue());
System.out.println("Cloned reference value: " + cloned.getReferenceObject().getValue());
}
}
class OriginalObject implements Cloneable {
private ReferenceObject referenceObject = new ReferenceObject("Initial Value");
public OriginalObject() {}
public void changeReferenceObject() {
referenceObject.setValue("Changed Value");
}
public ReferenceObject getReferenceObject() {
return referenceObject;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 深拷贝方法
public OriginalObject deepClone() {
try {
OriginalObject result = (OriginalObject) super.clone();
result.referenceObject = new ReferenceObject(this.referenceObject.getValue());
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 由于实现了Cloneable,此情况不应该发生
}
}
}
class ReferenceObject {
private String value;
public ReferenceObject(String value) {
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
输出结果:
Original reference value: Changed Value
Cloned reference value: Initial Value
通过手动实现深拷贝,我们确保了原始对象和克隆对象在引用对象的属性上保持了独立性。即使修改了原始对象的引用属性,克隆对象的相应属性也不会受到影响。
4.2.2 深拷贝与对象状态的完整复制
在需要完全复制对象状态时,深拷贝是必不可少的。特别是在对象图复杂、对象间有多个依赖关系时,深拷贝能够保证拷贝出来的对象是完全独立的,这对于很多应用场景来说是至关重要的。
深拷贝的重要性主要体现在以下场景:
- 并发编程 :在多线程环境中,为了防止对象状态在不同线程间共享导致的并发问题,需要对对象进行深拷贝。
- 持久化 :在对象需要持久化到数据库时,往往需要存储对象的完整状态,而不是对象的引用。
- 逻辑完整性 :在某些业务逻辑中,对象需要与其他对象保持独立性,以确保逻辑的完整性和数据的一致性。
深拷贝的挑战主要在于需要对对象的所有成员变量进行完整的检查和复制,这可能会导致效率降低。如果对象图过于复杂,递归复制的开销可能会很大。因此,在选择实现深拷贝时,需要权衡性能和逻辑需求。
在本节中,我们深入探讨了浅拷贝和深拷贝的定义、工作原理以及潜在的影响。对于开发者来说,理解浅拷贝和深拷贝的区别以及它们各自的使用场景,是确保代码质量和程序正确性的关键。在下一节中,我们将进一步讨论clone方法的权限控制以及异常处理,以确保在实际开发中,我们能够更加有效地利用Java对象的克隆机制。
5. clone方法的权限控制与异常处理
在处理对象克隆时,我们不仅需要关注如何正确地实现clone方法,还需要注意权限控制和异常处理,确保克隆过程的安全性和健壮性。
5.1 权限控制在clone方法中的应用
5.1.1 如何选择正确的访问权限
clone方法的访问权限对于对象的克隆安全性至关重要。默认情况下,Object类的clone方法是受保护的。然而,当我们实现Cloneable接口时,通常需要覆写这个方法,并决定它的访问级别。选择正确的访问权限需要基于你的类设计和对象克隆的安全要求。
通常情况下,有两种选择:
- public :允许任何对象调用你的 clone() 方法。这种方式提供了最大的灵活性,但同时你也放弃了对克隆过程的控制,可能带来安全风险。
- protected :仅允许子类和同一个包内的对象调用你的 clone() 方法。这种方式为继承提供了便利,同时通过限制调用范围增强了封装性。
选择合适的访问权限时,需要考虑以下几点:
- 对象的封装性 :如果你的对象设计为高度封装,保护克隆过程可能会更合适。
- 继承关系 :如果你的类设计为可以被继承,将clone方法定义为protected是常见的做法。
- 安全性 :对于需要严格控制克隆行为的类,public clone方法可能不是最佳选择。
5.1.2 权限控制对对象克隆安全性的保障
权限控制不仅决定了谁可以调用clone方法,它还影响克隆操作的安全性。通过控制访问权限,我们可以在某种程度上防止不恰当的克隆操作,特别是对于那些只应在内部克隆或者由子类克隆的对象。
例如,将clone方法设置为protected,可以阻止外部代码随意创建对象的副本,确保只有合适的子类能够进行克隆。同样,如果将clone方法定义为public,我们可以通过抛出CloneNotSupportedException来显式拒绝克隆操作,或者在clone方法内部进行权限检查,仅在满足特定条件时才执行克隆。
5.2 空指针异常在对象克隆中的处理
5.2.1 空指针异常产生的原因分析
在进行对象克隆时,空指针异常是一个常见的问题,特别是在处理包含其他对象引用的复杂对象时。空指针异常通常发生在被克隆对象的某个字段未被正确初始化时。
例如,考虑以下具有对象引用的类:
public class Person implements Cloneable {
private String name;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
Person newPerson = (Person) super.clone();
newPerson.address = (Address) address.clone();
return newPerson;
}
}
class Address {
private String street;
private String city;
@Override
public Object clone() {
return super.clone();
}
}
如果在创建 Person 对象时,没有为 address 字段赋值(即 address 为 null ),那么调用 clone() 方法将抛出空指针异常,因为 null 引用没有被覆盖。
5.2.2 防止空指针异常的克隆实践
为防止空指针异常,你需要在克隆过程中增加适当的检查:
- 检查null值 :在尝试克隆任何引用之前,检查这些引用是否为
null。 - 字段初始化 :确保所有字段在使用前都被正确初始化。
- 编写防御性代码 :即使当前克隆逻辑不涉及复杂的对象图,也应该编写能够处理未来可能的变更的代码。
以下是改进后的克隆方法,增加了对null值的检查:
@Override
protected Object clone() throws CloneNotSupportedException {
Person newPerson = (Person) super.clone();
if (address != null) {
newPerson.address = (Address) address.clone();
} else {
newPerson.address = null; // 或者提供一个默认值
}
return newPerson;
}
通过这种方式,如果 address 字段为 null ,克隆操作不会抛出异常,而是将 newPerson.address 设置为 null ,或根据设计需求提供适当的默认值。这样的做法增强了代码的健壮性,使其更能抵抗未来可能出现的变更。
6. 对象可变性与不变性的平衡
6.1 可变对象与不可变对象的定义
6.1.1 可变对象的特点
可变对象是指在对象创建之后,其内部状态可以在对象生命周期内被修改的对象。Java中大部分的类都是可变的,比如 StringBuffer 、 Date 、 BigDecimal (在某些情况下)。可变对象具有以下特点:
- 可以通过对象提供的方法修改其内部状态。
- 状态的改变可能会影响依赖于该对象状态的其他对象。
- 可变对象在多线程环境下可能需要额外的同步措施以保证线程安全。
6.1.2 不可变对象的优势和限制
不可变对象是指一旦创建,其内部状态就不能改变的对象。在Java中, String 、 Integer 、 Double 等包装类都是不可变的。不可变对象具有以下优势:
- 线程安全:不可变对象可以在多线程环境中安全地共享,无需额外的同步措施。
- 简化设计:不可变对象更容易设计和实现,因为它们的状态不会改变。
- 缓存:不可变对象可以被缓存起来以避免重复创建相同的对象。
但是,不可变对象也有一些限制:
- 需要创建新对象来表示状态变化:一旦状态改变,就需要创建一个新的对象来代表新的状态,这可能导致额外的资源消耗。
- 潜在的性能影响:在频繁修改对象状态的场景下,不可变对象可能会产生大量的临时对象,影响性能。
6.2 克隆与对象状态的管理
6.2.1 如何在克隆中管理对象状态
在使用克隆方法管理对象状态时,需要考虑对象的可变性。克隆对象时,应当根据对象是否可变来决定是进行浅拷贝还是深拷贝。
对于可变对象,浅拷贝可能只复制了对象引用而非对象实际数据,导致原对象和克隆对象共享状态,从而产生意外的副作用。深拷贝能够完整地复制对象的数据,保证对象状态的独立性。下面是一个深拷贝的代码示例:
public class MyObject implements Cloneable {
private int data;
public MyObject(int data) {
this.data = data;
}
public Object clone() {
try {
MyObject copy = (MyObject) super.clone();
// 对于引用类型成员变量,需要进行深拷贝
copy.data = this.data;
return copy;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can't happen
}
}
}
在上述代码中,我们覆写了 clone 方法,并手动将成员变量 data 的值复制到了克隆的对象中。
6.2.2 不可变性原则在克隆中的应用
对于不可变对象,由于其状态不可改变,克隆操作相对简单。当克隆一个不可变对象时,可以直接返回一个新的对象,因为状态不会改变,不需要担心潜在的线程安全问题或状态一致性问题。
然而,需要注意的是,即便对象本身是不可变的,但是如果包含的成员变量是可变的,那么在实现克隆时,也需要对这些可变成员进行深拷贝。
public class ImmutableObject {
private final Date date;
public ImmutableObject(Date date) {
this.date = new Date(date.getTime()); // 使用构造函数进行深拷贝
}
public Date getDate() {
return new Date(date.getTime()); // 返回日期对象的深拷贝
}
}
在上述代码中, date 成员是可变的,我们在构造函数和 getDate 方法中都返回了 date 的一个新的拷贝,确保了 ImmutableObject 的不可变性。
使用不可变对象时,可以避免很多并发问题,但需要注意构造函数中成员变量的复制,保证外部无法影响到对象内部的状态。克隆方法的使用,也应遵循这一原则,确保对象在克隆后保持其不可变性。
通过以上分析,我们可以看到在对象可变性与不变性之间进行平衡的重要性,以及如何在克隆方法的实现中妥善管理对象状态。这样的处理不仅可以提高代码的健壮性,还可以在多线程环境下避免许多潜在的并发问题。
7. clone方法使用场景和替代方案
在编程实践中, clone 方法提供了一种方便的对象复制机制,但同时也带来了一些问题和限制。在本章节中,我们将探讨 clone 方法的使用场景,以及在一些特定情况下,克隆的替代方案。
7.1 clone方法的适用场景
7.1.1 clone方法的优势所在
clone 方法的一个显著优势是它的易用性。当我们需要创建一个对象的深拷贝时,通过覆盖 clone 方法,我们能够在不暴露对象内部细节的情况下,实现对象状态的完整复制。这在实现像图这样的数据结构时特别有用,其中节点和边的关系需要被复制。
class GraphNode implements Cloneable {
private List<GraphNode> neighbours;
@Override
protected GraphNode clone() {
try {
GraphNode result = (GraphNode) super.clone();
result.neighbours = new ArrayList<>(neighbours.size());
for (GraphNode node : neighbours) {
result.neighbours.add(node.clone());
}
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can never happen
}
}
}
在这个例子中,我们覆盖了 clone 方法以实现一个图节点的深拷贝。注意,我们在克隆节点时也克隆了节点的邻居列表。
7.1.2 clone方法的局限性和适用条件
尽管 clone 方法提供了一个方便的复制对象的方式,但它也有一些局限性。最大的问题之一是它要求类实现 Cloneable 接口,但这本身并不会做任何拷贝操作,它只是为 Object 类的 clone 方法提供了一个信号,表明该类允许实例被克隆。此外, clone 方法在没有适当覆写时,只能执行浅拷贝,这可能导致意外的副作用。
clone 方法应该只在确实需要对象精确复制时使用,而且当类的内部结构允许这样的复制时。一般来说, clone 方法不适合用于那些具有复杂内部状态的对象,特别是在这些状态中包含文件句柄、网络连接或线程等资源时。
7.2 克隆的替代方案探讨
7.2.1 复制构造函数的优势与限制
替代 clone 方法的一个常见选择是使用复制构造函数。复制构造函数是创建一个与原始对象拥有相同状态的新对象的方法,但它是通过调用类自身的构造函数来实现的。
class SomeObject {
private int someField;
public SomeObject(SomeObject other) {
this.someField = other.someField;
// 如果有其他字段,也在这里进行深拷贝
}
}
复制构造函数的主要优势是它直观且易于理解,也避免了需要处理 Cloneable 接口和 Object 类的 clone 方法。然而,复制构造函数不能简单地应用到继承体系中,特别是当超类没有提供复制构造函数时。此外,复制构造函数不会与现有的克隆方法冲突,因此在设计新类时,需要进行明确的选择。
7.2.2 使用拷贝工厂模式的实践案例
拷贝工厂模式是一种更为灵活的替代方案,它通过一个工厂方法来创建对象的拷贝。工厂方法可以被设计成返回任何类型的对象,包括接口类型,这就为创建对象的拷贝提供了一个统一的接口。
interface Cloneable {
SomeObject deepCopy();
}
class SomeObject implements Cloneable {
private int someField;
@Override
public SomeObject deepCopy() {
return new SomeObject(this);
}
private SomeObject(SomeObject other) {
this.someField = other.someField;
// 如果有其他字段,也在这里进行深拷贝
}
}
拷贝工厂模式的优点是它提供了更好的封装性,使得类的使用者不需要了解对象是如何被复制的。此外,它也支持在不同情况下使用不同的复制策略。然而,拷贝工厂模式需要更多的代码来实现,且可能需要在类中额外添加一个拷贝方法。
综上所述, clone 方法在某些特定场景下仍具有优势,但考虑到它的局限性和易用性问题,替代方案如复制构造函数和拷贝工厂模式可能会提供更为灵活和安全的对象复制机制。在选择克隆方法时,开发者应仔细权衡各种方案的优缺点,以找到最适合其项目需求的方法。
简介:Java中的 clone 方法用于创建对象的副本,涉及浅拷贝和深拷贝。本文深入探讨了 clone 的工作原理、实现步骤、使用注意事项和个人实践经验。在实现克隆时,类必须实现 Cloneable 接口并覆盖 Object 类的 clone 方法。正确使用 clone 需要考虑权限、异常处理、对象可变性与不变性等因素。尽管 clone 方法在实现对象副本方面快速,但可能复杂且容易出错,因此在实际开发中常考虑使用 copy-constructor 或 Builder 模式。
1137

被折叠的 条评论
为什么被折叠?



