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

时间:2015-03-31 06:39:50

标签: java copy-constructor

我正在浏览Copy Constructors,我已经浏览了堆栈中的链接和其他链接。但我不清楚以下几点。

  1. 为什么我们需要复制构造函数
  2. 我们何时需要复制构造函数
  3. 我的意思是我们需要使用Copy Constructor的确切情况或场景是什么。有人可以用一个例子解释或指出链接,这样我就可以明确地理解它们。

    以下是我经历的链接,以了解什么是复制构造函数。

    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。复制构造函数将创建一个类似的对象,其值为rollNo: 1name: avinash。但两者都是2个不同的对象,对on对象值的更改不会影响另一个对象。

    这里的问题是

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

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

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

8 个答案:

答案 0 :(得分:31)

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

  1. 如果您拥有具有许多属性的复杂对象,则使用复制构造函数
  2. 会更加简单
  3. 如果向类中添加属性,则只需更改复制构造函数即可将此新属性考虑在内,而不是更改其他构造函数的每次出现

答案 1 :(得分:30)

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

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

  1. Cloneable作为接口实际上并不提供任何方法。为了使其有效,您仍需要覆盖clone的{​​{1}}方法。这对于界面来说是非常违反直觉的。

  2. java.lang.Object返回clone。为了它的任何用途,你仍然需要进行类型转换。这很尴尬,可能会导致运行时错误。

  3. Object方法的记录很少。对于clone,如果最终字段指向可变对象,事情就会搞砸。

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

  5. 还有更多的优势(参见Joshua Bloch的 Effective Java 2e ),但这些是我发现的与我迄今为止所做的工作最相关的要点。

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

答案 2 :(得分:5)

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

你会打电话吗?

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

而不是这样做,请使用此

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

复制构造函数比Object.clone()方法具有许多优点,因为它们

  1. 不要强迫我们实现任何接口或抛出异常。
  2. 不要求任何演员。
  3. 不要求我们依赖于未知的对象创建机制。
  4. 不要求父母级遵守任何合同或实施任何合同。
  5. 允许我们修改最终字段。
  6. 让我们完全控制对象创建,我们可以在其中编写初始化逻辑。
  7. 详细了解Java Cloning - Copy Constructor versus Cloning

答案 5 :(得分:0)

通过复制构造函数我们可以使用诸如实现Cloneable接口和覆盖克隆方法之类的复杂内容来进行克隆。我们也不需要特别担心深度克隆。

但需要指出的是:

1.当我们实施Cloneable时,对于其他类/用户来说,这个类是'对象可以克隆。除此之外,其他类可能没有关于cloneable的明确信息。

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

3.当你不想让你的类可以克隆时,如果你有一个带有访问说明符public的写复制构造函数,那么它会导致其他类可以创建你的类对象的不安全感。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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