为啥我们需要复制构造函数以及何时应该在 java 中使用复制构造函数

Posted

技术标签:

【中文标题】为啥我们需要复制构造函数以及何时应该在 java 中使用复制构造函数【英文标题】:Why do we need copy constructor and when should we use copy constructor in java为什么我们需要复制构造函数以及何时应该在 java 中使用复制构造函数 【发布时间】:2015-06-04 09:51:10 【问题描述】:

我正在浏览复制构造函数,我也浏览了堆栈溢出和其他链接中的链接。但我不清楚以下几点。

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

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

以下是我通过链接了解什么是复制构造函数。

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

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

第二个链接解释了“为什么”和“在哪里”使用复制构造函数。但我还是不清楚。

下面是我的类Employee.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 个不同的对象,更改 on 对象的值不会影响另一个对象。

这里的问题是

当我们有一个构造函数比如

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

创建一个对象。我们可以调用相同的构造函数来创建另一个对象。但是为什么我们需要调用复制构造函数。我们什么时候需要调用它呢?请解释

【问题讨论】:

如果你没有看到创建这样的构造函数的意义,那么你就不需要它。 不管怎样,上面示例中的复制构造函数尝试直接访问私有字段。它应该是 employee.getRollNo() 而不是 employee.rollNoemployee.getName() 而不是 employee.name 1.我们没有。 2. 21 年的 Java 没用过。 3. Java 中真的没有“复制构造函数”这样的东西。复制构造函数是 C++ 编译器在按值传递或返回对象或分配对象时可以使用的东西。在 Java 中这些都不会发生。 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

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

    Cloneable 作为接口实际上并没有提供任何方法。为了使其生效,您仍然需要覆盖java.lang.Objectclone 方法。对于界面来说,这是一种非常违反直觉的用法。

    clone 返回一个Object。为了使它有任何用途,您仍然需要进行类型转换。这很尴尬,可能会导致运行时错误。

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

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

还有更多优点(请参阅 Joshua Bloch 的 Effective Java 2e),但这些是我发现与我目前所做的工作最相关的几点。

[1] Java 语言中实际上没有任何东西为深度复制提供默认构造。对象最多只能告诉程序员它们可以被深度复制,例如实现Cloneable 或提供复制构造函数。

【讨论】:

【参考方案3】:

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

你会打电话吗?

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

不要这样做,而是使用这个

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

【讨论】:

我可以正确调用常规构造函数。员工 copyOfEmployee = new Employee(1,avinash); .这也会创建与第一个对象具有相同值的 Employee 对象。 @AvinashReddy - 如果有 20 个属性怎么办?.. 如果您不知道 5 个属性的值并且必须从另一个实例中获取它们怎么办?【参考方案4】:

与 Object.clone() 方法相比,复制构造函数为我们提供了许多优势,因为它们

    不要强迫我们实现任何接口或抛出异常。 不需要任何强制转换。 不要求我们依赖未知的对象创建机制。 不要求父类遵循任何契约或实现任何东西。 请允许我们修改最终字段。 允许我们完全控制对象的创建,我们可以在其中编写初始化逻辑。

阅读更多Java Cloning - Copy Constructor versus Cloning

【讨论】:

【参考方案5】:

另一种情况是存储对象“历史”值。

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

【讨论】:

【参考方案6】:

通过 Copy 构造函数我们可以在不使用诸如实现 Cloneable 接口和覆盖 clone 方法等复杂的东西的情况下进行克隆。我们也无需特别担心深度克隆。

但要注意的点是:

1.当我们实现 Cloneable 时,向其他类/用户暗示该类的对象可以是可克隆的。没有这个,其他类可能没有关于可克隆的明确信息。

2.如果我们将复制构造函数设为私有,那么我们可以限制此类对象的克隆。那么这个拷贝构造函数只能用于初始化类本地新创建的对象,不能用于其他类的克隆。

3.当您不想让您的类可克隆但如果您编写了访问说明符为 public 的复制构造函数,那么它会导致其他类可以创建您的类的对象的不安全性。

【讨论】:

【参考方案7】:

复制构造函数实现了浅层和深层克隆机制,但使用复制构造函数优于克隆(使用 Cloneable 接口)的主要优点是:

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

【讨论】:

【参考方案8】:

考虑这个示例,其中超类提供了一个复制构造函数,以隐式强制开发人员手动将字段复制到父类。这对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);
    

【讨论】:

以上是关于为啥我们需要复制构造函数以及何时应该在 java 中使用复制构造函数的主要内容,如果未能解决你的问题,请参考以下文章

为啥从类构造函数调用的方法应该是最终的? [复制]

为啥复制构造函数应该在 C++ 中通过引用来接受它的参数?

为啥Java中的抽象类有构造函数? [复制]

为啥以及何时在 Spring 中使用 @Inject 而不是 @Autowired? [复制]

何时在 Java 中使用 Identity 函数? [复制]

为啥不应该使用 Number 作为构造函数? [复制]