为啥Java没有复制构造函数?
Posted
技术标签:
【中文标题】为啥Java没有复制构造函数?【英文标题】:Why doesn't Java have a copy constructor?为什么Java没有复制构造函数? 【发布时间】:2010-10-24 02:08:31 【问题描述】:为什么 Java 不支持 C++ 中的复制构造函数?
【问题讨论】:
这些是一些很好的解释,谢谢大家! 另请阅读“复制构造函数有什么问题?为什么要使用 Clonable 接口?” ***.com/questions/388304/… 【参考方案1】: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 中,它只是简单地复制 reference。对象的状态没有被复制,所以隐式调用复制构造函数是没有意义的。
这就是它的全部内容。
【讨论】:
+1。当我们其他人对对象层次结构一无所知时,您直接切入语法 - 并且可能在您这样做时回答了 OP 的 real 问题。 您可能想要编辑作业;您正在将 b2 分配给它自己。此外,“statemen tlike”在错误的地方有一个空格。 如果你定义它,你可能会说“java can”,在这种情况下。 如果 Blah 里面有一个非原始的怎么办?喜欢:public class Blah private A foo; //A is some class public Blah(Blah b) foo = b.foo; // this would not work would it ?
@Mr_and_Mrs_D 您的示例将是浅拷贝构造函数的实现,谨慎的编码人员会这样记录它。它可以正常工作——新的 Blah 实例将共享一个引用到被复制的现有 Blah 实例上的同一个 A 实例。可以通过在 A 类定义中添加复制构造函数来实现深复制构造函数,然后在 Blah 中将构造函数定义为 public Blah(Blah b) foo = new A(b.foo);
【参考方案2】:
来自Bruce Eckel:
为什么[复制构造函数]在 C++ 而不是 Java 中工作?
复制构造函数是一个基础 C++ 的一部分,因为它自动 制作对象的本地副本。然而 上面的例子证明它确实 不适用于Java。为什么?在 Java 中 我们操纵的一切都是 处理,而在 C++ 中你可以拥有 类似句柄的实体,你也可以 直接绕过物体。 这就是 C++ 复制构造函数 适用于:当你想采取 对象并按值传递它,因此 复制对象。所以它有效 在 C++ 中很好,但你应该保留 请注意,该方案在 Java 中失败, 所以不要使用它。
(我建议阅读整个页面——实际上,请改用here。)
【讨论】:
【参考方案3】:我认为这个问题的答案很有趣。
首先,我相信在 Java 中所有对象都在堆上,虽然您没有指针,但您确实有“引用”。引用具有复制语义,Java 在内部跟踪引用计数,以便其垃圾收集器知道可以安全地删除什么。
由于您仅通过可复制引用访问对象,因此您需要复制对象的实际次数大大减少(例如,在 C++ 中,仅将对象传递给函数(按值)会导致新对象被复制构造,在 Java 中只传递对对象的引用)。设计者可能认为 clone() 足以满足其余用途。
【讨论】:
我同意。复制构造函数确实解决了 C++ 中的内存管理问题。 投反对票,因为:* Java 不使用复制语义(用于对象)。传递对象不会克隆或复制对象,也不会修改引用计数 - 它只是传递引用。 * 复制语义与复制对该对象的引用的事实之间存在太多混淆。 在 C++ 中,您应该通过指针或引用传递这些对象,以尽量减少过度复制。这不是内存管理的问题,当您确实想要制作对象的深层副本时,这只是语言中的(小)句法差异。 @Arafangion,难道不是他的整个答案的一部分,java 不这样做,而是复制参考?反正由我 +1 @Arafangion 这就是Object.clone()
存在的原因。我也为我 +1【参考方案4】:
这只是我的意见(我相信有一个合理的答案)
当您按值发送或返回类的实例时,C++ 中的复制构造函数主要有用,因为此时复制构造函数被透明地激活。
由于在 Java 中所有内容都是通过引用返回的,并且 VM 面向动态分配,因此复制构造函数的复杂性确实没有理由。
此外,由于所有内容都是通过引用进行的,因此开发人员通常必须提供自己的实现并决定如何克隆字段。
【讨论】:
【参考方案5】:你猜他们认为你可以创建一个 clone() 方法来代替?
【讨论】:
【参考方案6】:确实如此。当浅拷贝没问题时,你有 [clone()](http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#clone()),当它们不是时,你必须像 C++ 一样实现深拷贝。
唯一的实质性区别是它是工厂方法而不是构造函数,但就灵活性和可测试性而言,这可能是一件好事。
【讨论】:
【参考方案7】:我不是一个 C++ 程序员,但我似乎记得关于“三个朋友”的规则 - 复制构造函数、赋值运算符和析构函数。如果你有一个,那么你可能需要全部三个。
所以也许语言中没有析构函数,他们不想包含复制构造函数?只是猜测。
【讨论】:
不完全。在 C++ 中,它更像是:如果您需要这三个中的一个(例如,复制构造函数),那么您很可能还需要另外两个,尽管您当时可能没有意识到。 另外,如果你不需要它们,你应该将它们声明为私有而不是实现它们。这将防止编译器替换它自己的“浅”复制版本...【参考方案8】:嗯,可以。它只是不会被隐式创建。如果我不得不猜测,这可能与 Java 对象总是堆分配的事实有关。
在 C++ 中,默认的复制构造函数是成员级别的浅拷贝。如果一个类拥有在堆上分配的内存(通过原始指针),这将导致副本与原始类共享内部,这不是您想要的。
想象一下Java有这种行为。任何具有对象字段的类(阅读:基本上所有对象)都会有错误的行为,您需要自己覆盖它。对于 99% 的情况,您没有为任何人省去任何麻烦。此外,您刚刚为自己创建了一个微妙的陷阱——假设您不小心忘记了覆盖默认的复制构造函数。如果它是默认生成的,并且您尝试使用它,编译器根本不会抱怨,但您的程序会在运行时出现异常。
即使他们创建了一个执行深度复制的默认复制构造函数,我也不确定这是否特别有用。无论如何,您不仅倾向于在 Java 中执行比 C++ 更少的副本,而且您并不总是希望对字段进行深度复制。
您刚刚拥有的对象,以及您持有引用的对象,因为您需要它们,但不负责,它们是相同的 - 只是字段。所有权和借款不是一流的概念。对于您拥有的对象,您需要深度复制它们(除非它们是不可变的,在这种情况下您不应该打扰),而对于您只持有引用的对象,您需要复制引用。
我会争辩说,只是盲目地深度复制所有内容的复制构造函数也不适合许多类。当然,这不仅仅是默认的浅拷贝。
【讨论】:
【参考方案9】:Java 有复制构造函数 注意: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; 类名(类名对象) datafield=object.datafield;//见下例 现在打电话
类名obj=新类名();
classname anotherObject=obj;//或classname anotherObject=new classname(obj)
课堂演示 私有 int 长度; 私有宽度; 私有int半径; 演示(int x,int y) 长度=x; 宽度=y; 整数区域() 返回长度*宽度; //复制构造函数 演示(演示对象) 长度=obj.长度; 宽度=obj.宽度; 公共静态无效主(字符串参数 []) 演示 d1=新演示(5,6); demo d2=new demo(d1);//调用复制结构 System.out.println("d1 对象的区域="+d1.area()); System.out.println("d2 对象的区域="+d2.area());【讨论】:
以上是关于为啥Java没有复制构造函数?的主要内容,如果未能解决你的问题,请参考以下文章
如果类具有参数化构造函数,为啥Java不提供默认构造函数? [复制]
为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?