Java 中的复制构造器

原文:https://www.studytonight.com/java-examples/copy-constructor-in-java

复制构造器通过使用现有对象的值来创建新对象。复制构造器是其他克隆方法的替代方法,如可克隆接口。这些构造器可以创建浅克隆和深克隆。

在本教程中,我们将学习如何在 Java 中创建复制构造器。

复制简单类的构造器

复制构造器,就像任何其他构造器一样,应该与类同名。复制构造器将类的对象作为参数。它使用输入对象的值初始化字段。让我们为一个简单的学生类创建一个复制构造器,它包含一个字符串名称字段和一个双 GPA 字段。

class Student
{
    private String name;
    private double gpa;

    //parameterized constructor
    Student(String name, double gpa)
    {
        this.name = name;
        this.gpa = gpa;
    }    
    //Copy Constructor
    Student(Student s)
    {
        this.name = s.getName();
        this.gpa = s.getGpa();
    }    
    //getters and setters
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getGpa() {
        return gpa;
    }
    public void setGpa(double gpa) {
        this.gpa = gpa;
    }
}
public class Demo
{    
    public static void main(String[] args)
    {
        Student s1 = new Student("Justin", 8.5);
        Student cloneOfS1 = new Student(s1);

        s1.setName("Jessica");//Changing the original object

        System.out.println("Student-1: " + s1.getName());
        System.out.print("Student-2: " +  cloneOfS1.getName());
    }
}

学生一:杰西卡 学生二:贾斯汀

我们可以看到,复制构造器创建了 s1 Student 对象的克隆。更改 s1 的值不会影响克隆的对象。

复制具有引用类型的类的构造器

在上一节中,复制构造器创建了一个浅克隆。但是由于 Student 类只包含原始类型或不可变字段,所以克隆不受原始对象变化的影响。

如果类字段是原始类型或不变的,那么浅层克隆和深层克隆是一样的。让我们在学生类中添加一个地址类字段,并从我们的构造器中创建一个浅拷贝。

class Student
{
    private String name;
    private double gpa;
    private Address studentAddress;

    //parameterized constructor
    Student(String name, double gpa, Address add)
    {
        this.name = name;
        this.gpa = gpa;
        this.studentAddress = add;
    }    
    //Copy Constructor
    Student(Student s)
    {
        this.name = s.getName();
        this.gpa = s.getGpa();
        this.studentAddress = s.getStudentAddress();
    }    
    //getters and setters
    public Address getStudentAddress() {
        return studentAddress;
    }

    public void setStudentAddress(Address studentAddress) {
        this.studentAddress = studentAddress;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getGpa() {
        return gpa;
    }
    public void setGpa(double gpa) {
        this.gpa = gpa;
    }
}
class Address
{
    int postalCode;
    Address(int s)
    {
        this.postalCode = s;
    }
}
public class Demo
{    
    public static void main(String[] args)
    {
        Address add = new Address(10012);
        Student s1 = new Student("Justin", 8.5, add);
        Student cloneOfS1 = new Student(s1);

        add.postalCode = 20012;//Altering the Address will affect the cloned object

        System.out.println("Student-1: " + s1.getStudentAddress().postalCode);
        System.out.print("Student-2: " +  cloneOfS1.getStudentAddress().postalCode);
    }
}

学生-1: 20012 学生-2: 20012

要创建深度副本,我们需要在构造器中创建一个新对象。更改引用不起作用。学生类的修改后的复制构造器如下所示。

//Copy Constructor
Student(Student s)
{
    this.name = s.getName();
    this.gpa = s.getGpa();
    //creating a new address object
    this.studentAddress = new Address(s.getStudentAddress().postalCode);
}

让我们使用上面的构造器并查看结果。

public class Demo
{    
    public static void main(String[] args)
    {
        Address add = new Address(10012);
        Student s1 = new Student("Justin", 8.5, add);
        Student cloneOfS1 = new Student(s1);

        add.postalCode = 20012;

        System.out.println("Student-1: " + s1.getStudentAddress().postalCode);
        System.out.print("Student-2: " +  cloneOfS1 .getStudentAddress().postalCode);
    }
}

学生-1: 20012 学生-2: 10012

如上所示,更改原始对象不会影响克隆。这表明已创建深度拷贝。

复制构造器与克隆()方法

如前几节所述,覆盖可克隆接口的 clone()方法也可以创建深度拷贝。但是,使用复制构造器创建深度复制有一些优势。

  1. 创建和理解复制构造器要简单得多。使用可克隆接口,我们需要覆盖 clone()方法,并处理 CloneNotSupportedException。
  2. clone()方法返回一个对象引用,需要显式强制转换。
  3. 我们无法使用克隆方法将该值分配给最终字段。但是可以使用复制构造器来完成。

复制构造器的继承问题

复制构造器的一个缺点是它们不能被子类继承。如果使用父类引用子Class类型,那么我们就不能使用子类的复制构造器。下面的代码演示了这一点。

class ClassA
{
    int x;
    ClassA(int a)
    {
        this.x = a;
    }
    //copy constructor of Parent class
    ClassA(ClassA object)
    {
        this.x = object.x;
    }
}

class ClassB extends ClassA
{
    int y;
    ClassB(int a, int b)
    {
        super(a);
        this.y = b;
    }
    //copy constructor of child class
    ClassB(ClassB object)
    {
        super(object.x);
        this.y = object.y;
    }
}
public class Demo
{    
    public static void main(String[] args)
    {
        ClassA b = new ClassB(100, 200);//Assigning child type to parent reference
        ClassB cloneB = new ClassB(b);//Compilation Error
    }
}

线程“main”Java . lang . error:未解决的编译问题: 在 Demo.main(Demo.java:37)的构造器 ClassB(ClassA)未定义

我们可以通过使用显式强制转换来解决这个问题。但是如果对象不是子类,我们可能会得到一个 ClassCastException

public class Demo
{    
    public static void main(String[] args)
    {
        ClassA b = new ClassB(100, 200);
        ClassB cloneB = new ClassB( (ClassB) b);//Using a cast
    }
}

我们还可以在两个类中使用一个额外的方法来创建一个副本。这些方法将简单地调用它们的类的复制构造器。

class ClassA
{
    int x;
    ClassA(int a)
    {
        this.x = a;
    }
    //copy constructor
    ClassA(ClassA object)
    {
        this.x = object.x;
    }
    // copyObject() method that uses the copy constructor
    public ClassA copyObject()
    {
        return new ClassA(this);
    }
}

class ClassB extends ClassA
{
    int y;
    ClassB(int a, int b)
    {
        super(a);
        this.y = b;
    }
    //copy constructor
    ClassB(ClassB object)
    {
        super(object.x);
        this.y = object.y;
    }
    // copyObject() method that uses the copy constructor
    public ClassB copyObject()
    {
        return new ClassB(this);
    }
}

public class Demo
{    
    public static void main(String[] args)
    {
        ClassA b = new ClassB(100, 200);
        ClassA cloneB = b.copyObject();
    }
}

摘要

复制构造器是克隆对象的一种简单而优雅的方式。它们可以创建浅拷贝,也可以创建深拷贝。对于浅层克隆,我们只需要设置对输入对象字段的引用。但是对于深度拷贝,我们需要创建和分配新的对象。

复制构造器的一个缺点是它们是继承的。但是,我们可以通过在父类和子类中添加一个调用复制构造器的方法来克服这个缺点。