【问题标题】:In Java, how super.clone() method "knows" which object has called it?在 Java 中, super.clone() 方法如何“知道”哪个对象调用了它?
【发布时间】:2020-05-20 19:56:34
【问题描述】:

我真的对此感到困惑(也许这对您来说听起来很奇怪,对此感到抱歉)。例如,如果我有 A 类并覆盖克隆方法,并且在正文中我有 super.clone().. 它从 Object 类调用 clone() 方法,我的问题是 Object 类中的克隆方法如何知道要克隆哪个对象。 . 可能我不是很懂 super(我知道什么时候用这个你就知道用当前类的对象)。

【问题讨论】:

  • clone() 的实现是“神奇的”——它不是普通的 Java 代码可以实现的,它内置在语言中(真的,在 JVM 中)。
  • 它的黑巫术,直接在JVM中实现,而不是Java代码。最重要的是,它相当糟糕并且有很多问题。只是不要使用它,更喜欢显式的复制构造函数。

标签: java inheritance clone super


【解决方案1】:

所有 Java 类,如果它们没有明确定义 extends,则扩展 Object。对super.clone() 的调用或对super.someMethod() 的任何调用都将调用类层次结构中的一个链接的该方法的实现,而不是调用该类中定义的该方法的实现。

举个例子

public class A {
    public void someMethod(){
        System.out.println("A.someMethod()");
    }
}

public class B extens A {
    @Override
    public void someMethod(){
        super.someMethod(); //call the implementation of someMethod() as defined in A
        System.out.println("B.someMethod()");
    }
}

这个:

new A().someMethod();

将打印A.someMethod();

还有这个:

new B().someMethod();

将打印

A.someMethod()
B.someMethod()

编辑 1

一个稍微复杂一点的例子:

public class SuperTest {

    public static class A {

        public void someMethod() {
            System.out.println(
                    this.getClass().getSimpleName()+".someMethod()");
        }
    }

    public static class B extends A {

        @Override
        public void someMethod() {
            super.someMethod();
        }
    }

    public static void main(String[] args) {
        new B().someMethod();
    }
}

将打印B.someMethod()

super 用于调用类层次结构更高的实现,但仍会在执行super 调用的对象上调用它。


编辑 2

因为这涉及到clone(),它本身并不是很好,应该不惜一切代价避免。您可以在整个互联网上阅读原因。

clone() 的规范实现确实需要 Class 的类层次结构中的所有类提供 clone() 的规范实现。 clone() 的实现应该从调用super.clone() 开始,因为我们期望父类将克隆该类的所有成员并分配它们并返回对象。然后子类的clone() 实现应该复制其成员字段并将它们分配给克隆。

我们可以在扩展示例中看到这种情况

public class SuperCloneTest {

    public static class A implements Cloneable {

        private String member1;

        public A(String value) {
            this.member1 = value;
        }

        @Override
        public Object clone() {
            System.out.println("In A.clone()");
            System.out.printf("Class of `this` in A.clone(): %s\n", this.getClass().getSimpleName());
            A clone;
            try {
                clone = (A) super.clone();

                System.out.printf("Class of clone in A.clone(): %s\n", clone.getClass().getSimpleName());
                System.out.printf("Value of member1 in A.clone(): %s\n", this.member1);

                clone.member1 = this.member1;

                return clone;
            } catch (CloneNotSupportedException e) {
                //A implements Cloneable so we can be sure this won't happen
                throw new UnsupportedOperationException(e);
            }
        }
    }

    public static class B extends A {

        private String member2;

        public B(String value1, String value2) {
            super(value1);
            this.member2 = value2;
        }

        @Override
        public Object clone() {
            System.out.println("In B.clone()");
            System.out.printf("Class of `this` in B.clone(): %s\n", this.getClass().getSimpleName());

            B clone = (B) super.clone();
            System.out.printf("Class of clone in B.clone(): %s\n", clone.getClass().getSimpleName());
            System.out.printf("Value of member2 in B.clone(): %s\n", this.member2);

            clone.member2 = this.member2;

            return clone;
        }
    }

    public static class C extends B {

        private String member3;

        public C(String value1, String value2, String value3) {
            super(value1, value2);
            this.member3 = value3;
        }

        @Override
        public Object clone() {
            System.out.println("In C.clone()");
            System.out.printf("Class of `this` in C.clone(): %s\n", this.getClass().getSimpleName());

            C clone = (C) super.clone();
            System.out.printf("Class of clone in C.clone(): %s\n", clone.getClass().getSimpleName());
            System.out.printf("Value of member3 in C.clone(): %s\n", this.member3);

            clone.member3 = this.member3;

            return clone;
        }
    }

    public static void main(String[] args) {

        new C("value1", "value2", "value3").clone();
    }
}

当我们运行它时,我们得到:

In C.clone()
Class of `this` in C.clone(): C
In B.clone()
Class of `this` in B.clone(): C
In A.clone()
Class of `this` in A.clone(): C
Class of clone in A.clone(): C
Value of member1 in A.clone(): value1
Class of clone in B.clone(): C
Value of member2 in B.clone(): value2
Class of clone in C.clone(): C
Value of member3 in C.clone(): value3

在这里我们可以看到this 总是C,克隆也是。一直向上堆栈并在A 中调用super.clone() 调用Object.cone(),它将分配与this 相同类型的对象。

【讨论】:

  • 感谢您的回答。我理解这一点,但在这个例子中,我们只是调用父类方法并打印一些东西,我们不必知道哪个对象调用了它。但是在克隆方法的情况下,它必须知道哪个对象调用了该方法才能克隆它。我希望你能理解我的困惑。提前致谢。
  • 实际上 Object 类如何知道子类的成员以克隆该成员。我认为这就像我们从 A 类访问 B 类中的数据一样,这是不可能的,因为父类不知道子类中有哪些附加数据。也许这样我可以更好地解释我的困惑。
  • @DevPer 我添加了更多示例和解释。希望这可以解决问题。
【解决方案2】:

Object 中的 clone 方法是默认实现。

【讨论】:

  • 感谢您的回答。我理解这一点,但是在那个覆盖方法中,我们通常调用 super.clone() 方法来进行浅拷贝。在这种情况下,我不明白 Java 是如何知道哪个对象从 Object 类调用了这个克隆方法的,因为我们只是说 super。 clone(),我认为这只是从父(对象)类调用克隆方法,我们不告诉克隆哪个对象。
  • P.S.我理解当直接调用 object.clone() 方法时,这种方式对我来说是逻辑的,因为通过这种方式,来自 Object 类的 clone 方法知道要克隆哪个对象,因为我们用某个对象调用 clone 方法。但是在 super 中。 clone() 我们没有指定哪个对象调用它。希望您能理解我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多