【问题标题】:When do superclasses not have a default constructor?超类什么时候没有默认构造函数?
【发布时间】:2018-07-01 17:56:20
【问题描述】:

根据Java tutorial on constructors

你不必为你的类提供任何构造函数,但是你 执行此操作时必须小心。编译器自动提供一个 无参数,任何没有构造函数的类的默认构造函数。 此默认构造函数将调用 超类。在这种情况下,编译器会抱怨,如果 超类没有无参数构造函数,因此您必须验证 它确实如此。如果你的类没有明确的超类,那么它有一个 Object 的隐式超类,它确实没有参数 构造函数。

如果您有一个没有显式默认构造函数的超类A
和一个没有显式默认构造函数的子类B extends A

在驱动程序类的 main 方法中你可以这样做
A obj1 = new A();
将创建一个默认构造函数,
它将调用 Object 类的默认构造函数,对吗?

但如果你这样做
B obj2 = new B();
根据教程,将为B生成一个默认构造函数,
并且该构造函数将调用超类的无参数构造函数,
将依次调用Object中的构造函数。

那么超类什么时候没有无参数构造函数呢?

【问题讨论】:

    标签: java class inheritance constructor


    【解决方案1】:

    默认构造函数在你不提供时由编译器定义。

    所以这个

    public class A{}
    

    编译器会表示为:

    public class A
      public A() {
        super(); //invokes Object's default constructor
      }
    }
    

    由于我对A 的定义没有定义显式构造函数。

    在上面的示例中,A 隐式扩展了Object,而Object's 默认构造函数在super() 执行时由编译器自动调用。对于任何可能扩展 A 的类也是如此,例如:

    public class B extends A {}
    

    将由编译器实现,有点像:

    public class B extends A {
       public B() {
          super(); //invokes A's default constructor
       }
    }
    

    如您所见,最终会链接Object 的默认构造函数,然后是A 的默认构造函数,最后是B 的默认构造函数。

    > 那么超类什么时候会没有无参数构造函数呢?

    当您明确定义一个无参数构造函数时,它不会有。例如,如果我将A 的定义更改为

    public class A {
       public A(String name){}
    }
    

    然后A 不再有默认构造函数,我不能再这样做了

    public class B extends A {
       //Uh oh, compiler error. 
       //Which parent class constructor should the compiler call?
    } 
    

    现在B 必须通过明确说明要使用哪个构造函数,从其父类中显式链接正确的构造函数。例如

    public class B extends A {
       B() {
         super("B"); //Now the compiler knows which constructor to invoke
       }
    }
    

    Java 反编译器演示

    实际上,您可以使用 JDK 附带的工具来演示所有这些。在您的 JDK bin 目录中有一个名为 javap 的程序。这是 Java Decompiler 工具,可以让您查看编译器生成的代码。

    您可以编译我的示例,然后反编译它们以查看生成的代码,例如

    javac A.java
    javap A
    

    反编译器会告诉你:

    public class A {
      A();
    }
    

    这清楚地表明编译器添加了一个默认构造函数。

    你可以反汇编类来查看字节码。

    javac B.java
    javap -c B
    

    它会显示它是如何调用父类默认构造函数的

    class B extends A {
      B();
        Code:
           0: aload_0
           1: invokespecial #1 // Method A."<init>":()V
           4: return
    }
    

    如果我给A的构造函数添加一个默认参数,你会看到编译器不再提供默认构造函数,它只是提供了我明确定义的那个:

    class A {
        A(String name){}
    }
    

    那我就可以了

    javac A.java
    javap A
    

    它会产生

    class A {
      A(java.lang.String);
    }
    

    这表明您在原始问题中引用的规范中阅读的内容是正确的。

    【讨论】:

      【解决方案2】:

      那么超类什么时候没有无参数构造函数呢?

      如果您添加任何其他超类构造函数但忘记添加无参数构造函数,编译器会报错。在这种情况下,不提供超类的默认构造函数。

      【讨论】:

        【解决方案3】:

        在驱动程序类的 main 方法中,您可以这样做

        A obj1 = new A();
        

        将创建一个默认构造函数

        您无法在驱动程序的main 方法中创建默认构造函数。如果定义了,就可以使用;如果未定义,则会出现编译错误。

        那么超类什么时候没有无参数构造函数呢?

        当它有其他构造函数时,所有这些构造函数都带有 一些 参数。这是一个例子:

        class SuperA {
            public SuperA(String str) { ... }
            public SuperA(int num) { ... }
        }
        

        在上面,SuperA 有两个构造函数 - 一个采用 String,另一个采用 int。它们都不是默认的,因为它们带有参数。

        如果你创建了一个派生类DerivedB extends SuperA并且没有定义任何构造函数,你会得到一个编译错误。

        【讨论】:

          猜你喜欢
          • 2019-03-01
          • 1970-01-01
          • 2023-03-20
          • 1970-01-01
          • 2021-12-12
          • 2016-06-05
          • 2022-01-01
          • 1970-01-01
          • 2016-07-18
          相关资源
          最近更新 更多