java 为什么我们需要复制构造函数,什么时候应该使用复制构造函数

Why do we need copy constructor and when should we use copy constructor in java

我正在学习复制构造函数,我还学习了堆栈溢出和其他方面的 links。但我不清楚以下几点。

  1. 为什么我们需要复制构造函数
  2. 我们什么时候需要复制构造函数

我的意思是我们需要使用复制构造函数的确切情况或场景是什么。有人可以用一个例子来解释或指出 links 以便我可以通过并清楚地理解它们。

以下是我为了理解什么是复制构造函数而经历的link。

http://www.programmerinterview.com/index.php/java-questions/how-copy-constructors-work/

https://deepeshdarshan.wordpress.com/2013/12/05/copy-constructors-in-java/

第二个link解释了'why'和'where'要使用复制构造函数。但是我还是不清楚。

下面是我的classEmployee.java

package com.test;

/**
 * @author avinashd
 *
 */
public class Employee {

    private String rollNo;
    private String name;

    //constructor
    public Employee(String rollNo, String name){

        this.rollNo = rollNo;
        this.name = name;
    }

    //copy constructor
    public Employee(Employee employee){

    this.rollNo = employee.rollNo;
    this.name = employee.name;

    }

    public String getRollNo() {
        return rollNo;
    }

    public void setRollNo(String rollNo) {
        this.rollNo = rollNo;
    }

    public String getName() {
        return name;
    }

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

复制构造函数用于创建与现有对象具有相同值的对象的精确副本。

例如,假设我们有一个值为 rollNo: 1name: avinash 的员工。 Copy Constructor 会创建一个类似的对象,其值为 rollNo: 1name: avinash 。但两者都是 2 个不同的对象,更改对象的值不会影响另一个对象。

这里的问题是

当我们有一个构造函数时

public Employee(String rollNo, String name){
    this.rollNo = rollNo;
    this.name = name;
}

创建对象。我们可以调用同一个构造函数来创建另一个对象。但是为什么我们需要调用 copy constructor.When 我们需要调用它吗?。请解释

如果您想要另一个 Employee 实例与您已有的实例具有完全相同的值怎么办?

你会打电话吗?

setName(oldEmployee.getName())..
setRollNumber(oldEmployee.getRollNumber())..
etc..

不用那样做,用这个

Employee copyOfEmployeeOne=new Employee(employeeOneInstance);
// no need of a sequence of setters..

使用复制构造函数而不是传递所有参数的构造函数有两个很好的理由:

  1. 当你有一个具有许多属性的复杂对象时,使用复制构造函数要简单得多
  2. 如果您向 class 添加一个属性,您只需更改复制构造函数以将此新属性考虑在内,而不是更改其他构造函数的每个出现

另一种情况是存储对象 "historical" 值。

所以你只有一个对象,但每当你改变它的状态时,你想将它添加到 ArrayList 或任何最适合你的数据结构,而不是只使用 [=16= 手动复制数据] 在进一步修改之前。

按照惯例,复制构造函数应该提供对象的深层复制。正如其他答案已经提到的,复制构造函数提供的主要便利是当您的对象变得太复杂时。请注意,java.lang.Cloneable 提供了(几乎)类似的声明。

但是在 Cloneable 接口上使用复制构造函数有很多优点。

  1. Cloneable 作为一个接口实际上并没有提供任何方法。要想生效,还是需要重写java.lang.Objectclone方法。这对于接口来说是一种违反直觉的用法。

  2. clone returns 一个 Object。要使它有用,您仍然需要进行类型转换。这很尴尬,可能会导致运行时错误。

  3. clone 方法的文档很少。对于 clone,如果您有指向可变对象的最终字段,事情就会变得一团糟。

  4. 最重要的是,复制构造函数可以接受和深度复制子类的实例。 IMO,这是复制构造函数真正发挥作用的地方。

还有更多优势(参见 Joshua Bloch 的 Effective Java 2e)但这些是我发现与我目前所做工作最相关的要点.

[1] Java 语言中没有任何内容实际上提供了深度复制的默认结构。至多,对象只能告诉程序员可以通过实现 Cloneable 或提供复制构造函数来深度复制它们。

复制构造函数给了我们许多优于 Object.clone() 方法的优点,因为它们

  1. 不要强迫我们实现任何接口或抛出异常。
  2. 不需要任何转换。
  3. 不要要求我们依赖未知的object创建机制。
  4. 不要求 parent class 遵守任何合同或执行任何事情。
  5. 允许我们修改最终字段。
  6. 允许我们完全控制object创建,我们可以在其中编写我们的初始化逻辑。

阅读更多关于 Java Cloning - Copy Constructor versus Cloning

通过复制构造函数我们可以进行克隆,而无需使用很多复杂的东西,比如实现 Cloneable 接口和覆盖克隆方法。我们也不需要特别担心深度克隆。

但需要注意的地方是:

1.When 我们实现了 Cloneable,这是对其他 classes/users 的一个暗示,这个 class' 对象可以被克隆。如果没有这个,其他 classes 可能没有关于 cloneable 的明确信息。

2.If 我们将复制构造函数设为私有,然后我们可以限制克隆此 class' 对象。那么这个复制构造函数只能用于初始化本地新创建的对象到 class 而不是用于在其他 class.

中的克隆目的

3.When 您不想让您的 class 可克隆,但是如果您编写了带有访问说明符 public 的复制构造函数,那么它会导致其他 class 不安全]es 可以创建 class.

的对象

Copy Constructors 实现了浅克隆和深克隆机制,但是使用copy constructor 相对于克隆(使用Cloneable 接口)的主要优点是:

  1. 在使用 Object.clone() 方法时,我们不需要任何类型的大小写。
  2. 我们将能够出于复制目的修改最终字段,这与我们在 Object.clone() 机制的情况下无法修改或访问最终字段不同。
  3. 它允许我们完全控制对象的创建,我们可以在其中编写我们的初始化逻辑。
  4. 如果我们想要克隆 class 的对象,该对象包含其他依赖 class 的引用变量,我们不需要实现 class 的克隆方法。只需初始化我们的复制构造函数,我们就可以实现。

考虑这个例子,其中 super class 提供了一个复制构造函数来隐式地强制开发人员手动将字段复制到父 class。这对 Downcasting:

很有用
public class Employee {
    private String name;
    private int age;

    // regular constructor
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // copy constructor
    public Employee(Employee that) {
        this.name = that.name;
        this.age = that.age;
    }
    // getters & setters
}

public class SeniorMgmt extends Employee {
    private boolean secureAccess;

    public SeniorMgmt(Employee employee, boolean secureAccess) {
        super(employee);
        this.secureAccess = secureAccess;
    }
    // getters & setters
}

public class TestDrive {
    public static void main(String[] args) {
        Employee programmer = new Employee("John", 34);
        SeniorMgmt promotedProgrammer = new SeniorMgmt(programmer, true);
    }
}