为什么 Java 没有复制构造函数?

Why doesn't Java have a copy constructor?

提问人:Cuga 提问时间:5/6/2009 最后编辑:T.J. CrowderCuga 更新时间:1/19/2019 访问量:35020

问:

为什么 Java 不支持像 C++ 那样的复制构造函数?

Java 复制构造函数

评论

0赞 Cuga 5/6/2009
这些都是一些很好的解释,谢谢大家!
0赞 Johannes Schaub - litb 5/6/2009
另请阅读“复制构造函数有什么问题?为什么要使用可克隆接口?stackoverflow.com/questions/388304/......

答:

1赞 Ryan 5/6/2009 #1

猜猜他们认为你可以做一个 clone() 方法吗?

10赞 dicroce 5/6/2009 #2

我认为这个问题的答案非常有趣。

首先,我相信在 Java 中,所有对象都在堆上,虽然你没有指针,但你确实有“引用”。引用具有复制符号,java 内部跟踪引用计数,以便其垃圾收集器知道哪些是安全的。

由于您只能通过可复制的引用访问对象,因此需要复制对象的实际次数大大减少(例如,在 C++ 中,仅将对象传递给函数(按值)会导致复制构造新对象,而在 Java 中仅传递对对象的引用)。设计人员可能认为 clone() 足以满足剩余的用途。

 

评论

2赞 alphazero 5/6/2009
我同意。复制构造函数确实解决了 C++ 中的内存管理问题。
0赞 Arafangion 5/6/2009
投了反对票,因为: * Java 不使用复制语义(用于对象)。传递对象不会克隆或复制对象,也不会修改引用计数 - 它只是传递引用。* 复制语义与复制对该对象的引用这一事实之间有太多的混淆。
0赞 Mike Kale 5/6/2009
在 C++ 中,您还应该通过指针或引用传递这些对象,以尽量减少过多的复制。这不是内存管理的问题,它只是当你想要制作一个对象的深度副本时,语言中的(小)语法差异。
0赞 Johannes Schaub - litb 5/6/2009
@Arafangion,难道不是他整个答案的一部分,java不这样做,而是复制引用吗?反正+1
0赞 Soutzikevich 1/19/2019
@Arafangion,这就是存在的原因。+1 也是我Object.clone()
2赞 Uri 5/6/2009 #3

这只是我的意见(我相信有一个合理的答案)

C++ 中的复制构造函数主要在按值发送或返回类实例时有用,因为这是透明激活复制构造函数的时候。

由于在 Java 中,所有内容都是通过引用返回的,并且 VM 面向动态分配,因此实际上没有理由解释复制构造函数的复杂性。

此外,由于一切都是通过引用进行的,因此开发人员通常必须提供自己的实现和如何克隆字段的决定。

0赞 Aaron Maenpaa 5/6/2009 #4

确实如此。当浅拷贝没问题时,你有[clone()](http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#clone()),当它们不行时,你必须像C++一样实现深拷贝。

唯一的实质性区别是它是一种工厂方法,而不是构造函数,但就灵活性和可测试性而言,这可能是一件好事。

0赞 David Hodgson 5/6/2009 #5

我不太喜欢C++程序员,但我似乎记得关于“三个朋友”的规则 - 复制构造函数,赋值运算符和析构函数。如果你有一个,那么你可能需要这三个。

因此,也许语言中没有析构函数,他们不想包含复制构造函数?只是一个猜测。

评论

0赞 Dan Breslau 5/6/2009
差一点。在 C++ 中,它更像是:如果你需要这三个中的一个(比如,一个复制构造函数),那么你很可能还需要另外两个,尽管你当时可能没有意识到这一点。
1赞 dicroce 5/6/2009
此外,如果您不需要它们,则应将它们声明为私有的,而不是实现它们。这将防止编译器替换它自己的“浅层”复制版本......
14赞 Dan Breslau 5/6/2009 #6

来自布鲁斯·埃克尔(Bruce Eckel):

为什么[复制构造函数]在C++而不是Java中工作?

复制构造函数是一个基本的 C++的一部分,因为它会自动 创建对象的本地副本。还 上面的例子证明它确实如此 不适用于 Java。为什么?在 Java 中 我们操纵的一切都是一个 句柄,而在 C++ 中您可以拥有 类似句柄的实体,您还可以 直接传递对象。 这就是 C++ 复制构造函数的内容 适用于:当您想采取 对象并按值传入,因此 复制对象。所以它有效 在C++中很好,但你应该保持 请注意,这个方案在 Java 中失败了, 所以不要使用它。

(我建议阅读整个页面 - 实际上,从这里开始。

134赞 cletus 5/6/2009 #7

Java 确实如此。它们只是不像在 C++ 中那样隐式调用,我怀疑这是你的真正问题。

首先,复制构造函数无非是:

public class Blah {
  private int foo;

  public Blah() { } // public no-args constructor
  public Blah(Blah b) { foo = b.foo; }  // copy constructor
}

现在 C++ 将隐式调用复制构造函数,语句如下所示:

Blah b2 = b1;

在该实例中克隆/复制在 Java 中根本没有意义,因为所有 b1 和 b2 都是引用,而不是像 C++ 中那样的值对象。在 C++ 中,该语句会复制对象的状态。在 Java 中,它只是复制引用。对象的状态不会被复制,因此隐式调用复制构造函数是没有意义的。

这就是它的全部内容。

评论

19赞 Dan Breslau 5/6/2009
+1.当我们其他人都在盯着对象层次结构时,你直接切入了语法——并且可能在你这样做的时候回答了 OP 的真正问题。
1赞 Carl Manaster 5/6/2009
您可能想要编辑作业;您正在将 B2 分配给自身。此外,“政治家喜欢”在错误的地方有一个空间。
2赞 rogerdpack 8/25/2010
在这种情况下,如果你定义它,你可能会说“java can”。
4赞 Mr_and_Mrs_D 4/10/2011
如果 Blah 里面有一个非原始人怎么办?喜欢:public class Blah { private A foo; //A is some class public Blah(Blah b) { foo = b.foo; } // this would not work would it ? }
0赞 RobertB 8/3/2014
@Mr_and_Mrs_D 您的示例将是一个浅层复制构造函数的实现,谨慎的编码人员会将其记录下来。它适用于它是什么 - Blah 的新实例将在正在复制的 Blah 的现有 inst 上共享对同一 A 实例的引用。深层复制构造函数可以通过向 A 类定义添加复制构造函数来实现,然后在 Blah 中将构造函数定义为public Blah(Blah b) { foo = new A(b.foo); }
-1赞 Isabella Engineer 7/8/2013 #8

Java copy 构造函数
注意:代替 demo d2=new demo(d1),你可以编写 demo d2=d1
主要区别 b/w 两个
demo d2=new demo(d1) 表示新对象被创建,它是 分配的内存 但是
demo d2=d1 意味着只创建了引用变量 它使用对象 D1 的相同内存地址,因此未分配 D2 分离的内存。

复制构造函数的语法:
见下文 第一个示例 复制构造函数非常简单 :)) classname(int datafield)
//简单构造函数
{
this.datafield=datafield;
}

classname(classname object)
{ datafield=
object.datafield;//参见以下示例
}
现在用于调用

{

类名 obj=new classname();

classname anotherObject=obj;//或 classname anotherObject=new classname(obj)

}

 class demo
{
    private int length;

    private int breadth;

    private int radius;

    demo(int x,int y)

    {
        length=x;
        breadth=y;
    }
    int area()
    {
        return length*breadth;
    }

    //Copy Constructor
    demo(demo obj)
    {
        length=obj.length;
        breadth=obj.breadth;
    }


    public static void main(String args[])
    {
        demo d1=new demo(5,6);
        demo d2=new demo(d1);//Invokes Copy Constructure
        System.out.println("Area for d1 object="+d1.area());
        System.out.println("Area for d2 object="+d2.area());

    }
}

0赞 Kevin 8/30/2016 #9

嗯,它可以。它只是没有隐式创建。如果我不得不猜测,这可能与 Java 对象总是堆分配的事实有关。

在 C++ 中,默认复制构造函数是成员级浅表复制。如果一个类拥有堆上分配的内存(通过原始指针),这将导致副本与原始副本共享内部,这不是你想要的。

想象一下,Java 有这种行为。任何具有对象字段的类(阅读:基本上是所有字段)都会有错误的行为,您需要自己重写它。在99%的情况下,你没有给任何人带来任何麻烦。此外,您刚刚为自己创建了一个微妙的陷阱 - 想象一下,您不小心忘记覆盖默认的复制构造函数。如果它是默认生成的,并且您尝试使用它,编译器根本不会抱怨,但您的程序在运行时会行为不端。

即使他们制作了一个执行深度复制的默认复制构造函数,我也不确定这是否特别有用。无论如何,您不仅倾向于在 Java 中执行比 C++ 更少的副本,而且您并不总是想要深度复制字段。

您只拥有的对象,以及由于需要它们而持有引用但不负责的对象是相同的 - 只是字段。所有权和借款不是一流的概念。对于您拥有的对象,您需要深度复制它们(除非它们是不可变的,在这种情况下,您不应该打扰),对于您只持有引用的对象,您需要复制引用。

我认为,一个只是盲目地深度复制所有内容的复制构造函数也不适合许多类。不过,默认情况下肯定不仅仅是浅层复制。