复制构造函数会进行浅表复制吗?

时间:2018-09-18 12:33:20

标签: java deep-copy

我的问题很清楚。复制构造函数是否进行深复制?还是浅表?

这是我面临的情况:

我正在制作一个节点编辑器应用程序。我有一个抽象的Node类。在那,我有一个称为Create()的抽象方法。我也以此方式覆盖了所有子类中的该方法,

    public Node Create(){
    TestClass theTest = new TestClass();
    theTest.Name = "Test Node";
    theTest.Title = "Default Node";
    theTest.setSize(new Point2D.Float(250,200));
    System.out.print(theTest.getClass());
    return theTest;
}

我认为这应该复制很深。既然那行不通,我也尝试了这个。

public Node Create(Point2D location) {
    TestClass theTest = null;
    try {
        theTest = this.getClass().newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }

    if (theTest != null) {
        theTest.Name = "The Node";
        theTest.Title = "Defaul Node";
        theTest.setSize((new Point2D.Float(250,200)));
        theTest.Location = location;
    }

    return theTest;
}

然后将所有子类类型添加到列表中,并使用子类创建弹出菜单。用户可以单击它并添加一个新节点。这是添加节点的代码。 JMenuItem的MouseEvent调用此方法。

private void addNode(Node node){
    Node newNode = node.Create(locationPersistence);
    nodes.add(newNode);
}

但是没有运气。似乎创建的是浅表副本,而不是深表副本。当我添加第一个节点时,它看起来很好。但是,当添加相同类型的第二个节点时,第一个节点将从那里消失并重新出现在新位置。这是否意味着这是一个浅表副本。如果是这样,如何实现深拷贝?

3 个答案:

答案 0 :(得分:2)

首先,默认情况下,Java中没有诸如复制构造函数之类的东西。有一个Cloneable接口和clone()方法。但是默认情况下,该方法将进行浅表复制。

您的代码集链接到属性Point2D中两个对象的相同location对象引用。您需要创建Point2D对象的新实例,并在新对象中使用它。

答案 1 :(得分:0)

复制构造函数是指您的类包含一个接受自身实例作为参数的构造函数。参数用于创建该类的新实例,该实例的字段值与作为参数提供的实例类的值完全相同。

您的Node类必须具有这样的构造函数:

public class Node {
    public Node(Node n) {
       //copy all fields in Node n here
       //eg this.a = n.a
       //this.b = n.b etc
    }
}

然后,当您从Node继承时,您还需要在子类构造函数中调用此父方法:

public class TestClass extends Node {
    public TestClass(TestClass t) {
        super(t);
        //copy any additional fields that is only present in TestClass here
    }
}

现在,浅拷贝和深拷贝之间的区别。 浅拷贝是指一个参考设置为等于另一个参考。 例如:

Point2D a = new Point2D(50, 50);
Point2D b = a;

当您更改a的成员之一的值时,b也将受到影响。原因是a和b都是对同一对象的引用。

a.x = 100;
System.out.println(b.x == 100); //prints true

现在深拷贝是a和b都引用自己的实例。可以按照以下步骤进行操作:

Point2D a = new Point2D(50, 50);
Point2D b = new Point2D();
b.x = a.x
b.y = a.y

如果我现在输入:

a.x = 100

然后b.x不会更改为相同的值,而是保留最初存储在a中的先前值,在这种情况下为50。

System.out.println(b.x == 100); //prints false
System.out.println(b.x == 50); //prints true

如果要在构造函数中具有深层副本,则需要确保引用可变类的所有类成员都引用其自己的实例

答案 2 :(得分:0)

Java避免了深度复制。

对于不可变的String类,这没有问题,因为可以共享字符串。

对于旧的可变java awt Point2D.Float类,确实存在问题。将其替换为不可变的类可能会比深复制更好。 javafx.geometry.Point2D是不可变的。

对于可变数组,有一个问题。甚至final数组都可以从外部更改其元素。这里的建议是改为使用集合。

private final List<Point2D> points = new ArrayList<>();

public List<Point2D> getPoints() {
    return Collections.unmodifiableList<>(points);
}

使用以小写字母开头的字段和方法名称的Java约定。 Java对此非常严格。

C / C ++部分需要深度复制以将对象保留在本地堆栈中。

Java在某种程度上消除了对复制构造函数的需求,但是从历史上看,它对String失败:String具有无意义的复制构造函数,可能受intern()的启发,并且具有内部char数组。