【问题标题】:What will be the type of Object in Constructor chaining?构造函数链接中的对象类型是什么?
【发布时间】:2015-06-29 12:18:02
【问题描述】:

为什么这段代码只返回类 C 的名称。当我使用 this 时,它应该返回我使用它的构造函数的类的名称。

class A{
     public A() {
         System.out.println(this.getClass().getName());
     }
}
class B extends A{
    public B() {
        System.out.println(this.getClass().getName());
    }
}
class C extends B{
     public C() {
         System.out.println(this.getClass().getName());
     }
}
class Main{
    public static void main(String[] args){
         new C();
    }
}

【问题讨论】:

  • 此代码不返回任何内容。它没有main 函数,所以无法知道你到底在运行什么。
  • 所以您创建了一个C 类型的对象并期望三个不同的打印输出?这不是对象继承的工作方式。
  • 你会得到一个c消息,因为你重写了构造函数,并声明了一个c实例,
  • @Ahmad 你正在重写构造函数。不是真的。
  • @Ahmad 我认为构造函数在 java 中永远不会被覆盖。

标签: java inheritance constructor


【解决方案1】:

this.getClass().getName() 返回this 实例的运行时类型的名称。如果您创建 C 的实例 (new C()),它将始终返回“packagename.C”。无论是从C 类的代码中调用,还是从C 的超类中的代码调用,都没有关系。

【讨论】:

    【解决方案2】:

    一些相关点:

    1) 编译器将自动在任何不包含对超类构造函数的显式调用的构造函数中插入对无参数超类构造函数的调用。 (我鼓励您在编写代码时选择显式行为而不是隐式行为,但这是一个有争议的风格点)。无论如何,对于编译器,您的代码将如下所示:

    class A{
        public A() {
            System.out.println(this.getClass().getName());
        }
    }
    
    class B extends A{
        public B() {
            super();
            System.out.println(this.getClass().getName());
        }
    }
    
    class C extends B{
        public C() {
            super();
            System.out.println(this.getClass().getName());
        }
    }
    
    class Main{
        public static void main(String[] args){
            new C();
        }
    }
    

    这清楚地表明 System.out.println 被调用了三个不同的时间。

    2) 在构造函数中使用 this 来调用方法通常会导致非常奇怪的行为。例如:this。那是因为初始化是这样工作的:

    1) 调用 C 类构造函数。此时,为 C 类对象分配内存,对象元数据(包括其类、实现的接口)在 VM 内部填充。所有字段,包括继承的字段都被初始化为它们的默认值

    2) B 类构造函数被调用。在构造函数内部,this 指的是 C 类的对象,但在 C 构造函数中初始化的所有字段都不会被初始化。这将立即调用 A 类的构造函数。

    3) 类 A 构造函数运行。初始化此构造函数中设置的字段。构造函数执行方法和初始化并返回,堆栈传递回 B 构造函数。

    4) B 构造函数执行其方法并将控制权返回给 C 构造函数。

    5) C 构造函数返回。

    所以现在我们明白会发生什么了:这条链打印了 3 次 C。我怀疑你想要写的是:

        class A{
        public A() {
            System.out.println(A.class.getName());
        }
    }
    
    class B extends A{
        public B() {
            super();
            System.out.println(B.class.getName());
        }
    }
    
    class C extends B{
        public C() {
            super();
            System.out.println(C.class.getName());
        }
    }
    
    class Main{
        public static void main(String[] args){
            new C();
        }
    }
    

    这将打印出 A,B,C。

    【讨论】:

    • “在子类的构造函数中跳过 super() 调用的做法很糟糕” 幸运的是,这不是真的。顺便说一句:stackoverflow.com/questions/2712971/…
    • 嗯,人们可以在风格元素上存在分歧。我强烈主张您应该始终选择显式行为而不是隐式行为。就像在参数定义中添加 final 一样,我认为这对初级程序员有帮助,我鼓励这样做。
    • @phil_20686 我不同意你的观点,因为我是否使用 super() 并不重要在这两种情况下,输出都是相同的
    • 这里列出了不影响程序输出的其他事项:1) 单元测试 2) 文档和 3) 使用 @Override。希望你做所有这些事情......所以很明显,一个动作是否影响输出并不是一个非常明智的标准,它是否是一个好主意。它只是关于可读性的争论。
    【解决方案3】:

    首先,在调用new Child()时,由于Child类中没有声明无参构造函数,所以会简单的调用super(),调用的是Parent构造函数。

    然后,当执行 this.getClass().getName() 时,这里的 this 代表一个 Child 实例,这就是为什么你会得到“Child”作为结果。请记住,Object#getClass() 返回对象所属的最具体的类。更多来自here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-24
      • 1970-01-01
      • 2018-01-29
      • 1970-01-01
      • 1970-01-01
      • 2019-01-17
      • 2017-11-09
      • 2017-06-04
      相关资源
      最近更新 更多