【问题标题】:Why doesn't Java have a copy constructor?为什么Java没有复制构造函数?
【发布时间】:2010-10-24 02:08:31
【问题描述】:

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

【问题讨论】:

标签: java copy-constructor


【解决方案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()); } }

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-04-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-12-20
              • 2012-11-28
              • 2012-11-16
              相关资源
              最近更新 更多