为什么要在Java中重写clone方法

16

我对在想要克隆对象的类中重写克隆方法感到困惑。

Object类有一个受保护的对象方法,根据受保护的行为,即当一个方法受到保护时,它只能被该类本身、该类的子类或与该类在同一包中的类访问

由于Java中的每个类都是从Object类继承而来的,所以它应该具有克隆方法,但我们仍然被迫重写clone方法。这是为什么呢?

此外,我在某些地方读到过要重写克隆对象并使其公开的建议。我想知道,为什么需要这样做?

欢迎所有答案。


克隆方法的疯狂细节 - https://dev59.com/mXM_5IYBdhLWcg3w-4nw - Arun Manivannan
5个回答

14
由于Java中的每个类都是从Object继承而来,因此它应该具有clone方法,但我们仍然被迫覆盖clone方法。不过你并不需要强制覆盖clone方法。在继承中,当你继承一个类时,并不强制要求你覆盖它的方法。其修饰符为public或protected并没有太大的区别。但是,如果你想直接在超类引用上调用一个方法,则该方法必须是public的。受保护的方法只能通过继承来访问。也就是说,你只能通过子类引用来访问它们。或者,如果你覆盖了该方法,你可以通过super关键字来访问它们。
话虽如此,你不应该覆盖clone方法,因为它是有问题的。这是因为,在一个类被克隆之前,你需要实现Cloneable接口。然后你的类会使用Object类的clone方法进行克隆。因为,Cloneable接口并没有确切的克隆方法。更好的选择是使用拷贝构造函数(Copy Constructor)来代替。
public class A {
   private int data;
   public A() {
   }

   public A(A a) {
      this.data = a.data;
   }
}

如果需要更多细节,我建议阅读Joshua Bloch的《Effective Java》中的这一章节,它涵盖了使用clone方法的所有方面。

《Effective Java》- 条款 #11 - 明智地重写 clone 方法


正如你所写的“不,你不必强制重写克隆方法”,但如果没有重写克隆方法,我就不能直接在对象上调用克隆。为什么会这样? - Anand
1
@anand。此外,您不能直接在“Object”类的实例上调用“clone”方法,因为“clone”方法是“protected”的,并且只能通过“继承”在“基类”中访问。因此,您可以通过同一类的实例调用它。但请注意,您需要处理“CloneNotSupportedException”。 - Rohit Jain
到目前为止,Object类中的clone方法是受保护的。根据定义,受保护的方法可以在子类中使用,并且在Java中,每个类都是Object的子类,那么为什么我们不能直接在我的类对象上调用clone方法呢? - Anand
@Anand。你可以直接在类对象上调用克隆。你试过了吗?你遇到了什么问题? - Rohit Jain
1
@Joni。要克隆一个实例,我们需要实现“Cloneable”接口。然后我们实际上使用“Object”类的protected clone方法。因此,“Cloneable”只是标记接口,并没有自己的'clone'方法。因此,我们必须使用Object类的clone方法。所以,你不能仅仅因为一个对象实现了Cloneable就调用克隆方法。有关更多详细信息,我建议你查看我提供的书籍链接。那是理解这个问题的最佳资源。 - Rohit Jain
显示剩余2条评论

5
我建议阅读Joshua Bloch的Effective Java第二版,其中有一个很好的章节讨论了克隆。
我不建议这样做。我认为这是JDK 1.0的错误。这本书会让你更清楚。
我建议编写一个复制构造函数来获得你想要的:
public class Foo {
    private String name;
    public Foo(String name) { this.name = name; }
    public Foo(Foo f) { this.name = f.name; }  // copy ctor here.
}

复制构造函数在继承中无法正常工作,因此我们需要使用克隆。 - Steve Kuo
1
不起作用?那看起来是什么样子?我认为,除非每个人都表现出极大的谨慎,否则克隆在继承方面也不能“正常工作”。Joshua Bloch在《Effective Java》中对Cloneable没有说好话。 - duffymo

3
在许多情况下,克隆对象的行为和克隆后的对象应该是什么并不清楚。因此,如果您希望您的类可以被克隆,则需要显式地声明它,并覆盖clone方法使其变成公共方法。
可能没有意义使用clone的情况包括表示某些资源的类,例如网络连接或同步锁。如果这些对象可以被克隆,那么克隆对象的行为就不是很清楚了。例如,网络连接的克隆是否有自己的TCP/IP连接,还是以某种方式使用现有连接?

2

克隆是Object类中的protected方法,因此您可以在类内部访问它。

关于访问权限-当一个方法被保护时,只有类本身、类的子类或与类相同包中的类才能访问该方法。

我看到了一些关于克隆方法的误解

  1. clone()方法在Object类中是protected的,因此你不能在类外调用child.clone()等方法,除非你重写它并使访问权限为public
  2. Cloneable是标记接口,如果您不将类标记为Cloneable,则在调用clone()方法时会出现CloneNotSupportedException
  3. 如果一个类仅包含原始字段或对不可变对象的引用,则通常情况下不需要修改super.clone返回的对象中的任何字段。
  4. 按照惯例,应通过调用super.clone来获取返回的对象。如果一个类及其所有的超类(除了Object)都遵循这个惯例,那么就会有x.clone().getClass() == x.getClass()

方法签名如下

@Override
public Object clone() throws CloneNotSupportedException {
    return super.clone();
}

参考资料:

  1. Object#clone()方法
  2. Cloneable接口

-1
    Why we do override clone() in cloning process?
    //clone() in Object class is protected
    package java.lang;


    protected native Object clone()
            throws CloneNotSupportedException;

    java.lang is default import in our java applications.

Note: If parent and sub class are both in same package then the methods in parent class are directly accessible. If they are in different package,then in subclass we have to override the parent class methods to use.

    Note:Object class is in java.lang package,we are using it in different package,so we have to override the clone() which is protected in Object class


first we will look into Protected method behavior.here is sample program to understand this
    //this class is in com.anusha.clonetrial
    package com.anusha.clonetrial;

    public class A {

        public A()
        {

        }
        protected void disp1()
        {
            System.out.println("class a");
        }
        protected void disp2()
        {
            System.out.println("class a");
        }
    }
    //below classes are in com.anusha.Test
    package com.anusha.Test;
    import com.anusha.clonetrial.A;


    class AA {


        protected void disp1()
        {
            System.out.println("class aa");
        }

        protected void disp2()
        {
            System.out.println("class aa");
        }
    }

    //class B derived from AA which is present in the same package
    class B extends AA
    {

        void show()
        {


            System.out.println("class b");
        }
    }

    //class C derived from A which is present in the different package

    class C extends A
    {

        @Override
        protected void disp1()
        {
            super.disp1();
        }
        void show()
        { 
            System.out.println("class c");
        }
    }

    package com.anusha.Test;




    public class CloneTest {


        public static void main(String[] args) {
            B b=new B();
            C c=new C();
            b.disp1();
            b.disp2();
            c.disp1();
            c.disp2();//gives error because it is not overridden.


        }

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接