关于父类构造函数调用子类成员的困惑

问题:
 tij中的习题:  
  class   BaseWithPrint   {  
      public   BaseWithPrint()   {  
      p.println("BaseWithPrint()");  
      print();  
      }  
      public   void   print()   {  
          System.out.println("BaseWithPrint.print");  
      }  
  }  
   
  class   DerivedWithPrint   extends   BaseWithPrint   {  
      int   i   =   47;  
      public   DerivedWithPrint()   {  
      p.println("DerivedWithPrint()");  
      }  
      public   void   print()   {  
          System.out.println("i   =   "   +   i);  
      }  
  }  
   
  public   class   GetFile   {  
   
  public   static   void   main(   String   args[]   ){  
  DerivedWithPrint   dp   =   new   DerivedWithPrint();  
          dp.print();  
  }  
  }  
  输出结果如下:  
  在父类构造函数BaseWithPrint()中调用print(),既然是在父类的构造函数里,那么子类自然还没有构造,其成员函数按说也应该是不存在的呀!可是为什么这时可以调用子类的print()呢?  

回答:
一、运行时刻  
      a   +----+     -+---+-  
          |         |                  
          |         |       A  
          |         |        
      b   +----+     -+-     C  
          |         |          
          |         |       B  
          |         |  
      c   +----+     -+---+-  
   
  我先用这个图表示类及其子类在 内存 中的逻辑概念上的分布。  
  其中:  
  1)   图形中的方框表示类在内存中的存储范围。  
  2)   A表示从a点到b点的内存区  
  3)   B表示从b点到c点的内存区  
  4)   C表示从a点到c点的内存区  
   
  现在有一个类SuperClass及其子类SubClass被加载到内存。其中SuperClass加载于内存区A,那么SubClass占用哪一部分内存呢?  
   
  正确答案是C而不是B。  
   
  二,从代码角度观察  
  对于继承,我们首先应该这样理解,代码中使用继承,只是为了减少代码量(那位,先把砖头放下),而不管父类还是子类,都是相对独立的类。  
   
  当一 个子 类继承自其父类时,从代码角度,就是把其父类的代码重写一遍,然后再加上额外的代码,从而,成其子类。其实,这一 工作 是编译器完成的,当然也不是重写代码,但是我们可以这么直观的理解。  
   
  我按照这种思路改造你的DerivedWithPrint,得到下面的代码:  
   
  //   注意,去掉了extends  
  class   DerivedWithPrint   {  
      int   i   =   47;  
      public   DerivedWithPrint()   {  
   
          //来自BaseWithPrint  
          p.println("BaseWithPrint()");  
          print();  
   
          //   本地  
          p.println("DerivedWithPrint()");  
      }  
   
      public   void   print()   {          
          System.out.println("i   =   "   +   i);  
      }  
  }  
   
  这就是DerivedWithPrint类在运行时刻应该执行的命令及顺序。因为原来的DerivedWithPrint中也有print方法,所以覆盖了其父类的print方法。上述过程好像叫做类的扁平化。  
   
  注意,如果原DerivedWithPrint的print方法中有super.print()字样,那么扁平化之后的print方法应该这样:  
   
      public   void   print()   {  
          System.out.println("BaseWithPrint.print");  
          System.out.println("i   =   "   +   i);  
      }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java中,子类构造函数在创建对象时会默认调用父类的无参构造函数。如果父类中没有无参构造函数,那么子类构造函数必须在第一行显式的调用父类的有参构造函数子类构造函数调用父类构造函数的方式有两种: 1. 使用super关键字调用父类构造函数 使用super关键字调用父类构造函数时,需要在子类构造函数的第一行使用super关键字,并传递相应的参数,如: ``` public class Son extends Father { public Son(String name, int age) { super(name, age); // 子类构造函数其他操作 } } ``` 2. 在父类中定义有参构造函数,并在子类构造函数调用父类中定义有参构造函数时,子类构造函数不必使用super关键字调用父类构造函数,只需在子类构造函数中传入相应的参数即可,如: ``` public class Father { String name; int age; public Father(String name, int age) { this.name = name; this.age = age; } } public class Son extends Father { public Son(String name, int age) { // 子类构造函数中不需要调用super关键字 // 父类中有参构造函数可以直接使用 this.name = name; this.age = age; // 子类构造函数其他操作 } } ``` 以上就是子类构造函数调用父类构造函数的两种方式,具体使用哪种方式要根据实际情况来决定。 ### 回答2: 在面向对象的编程中,子类继承了父类的属性和方法,并且可以在此基础上添加额外的属性和方法。在子类构造函数中,我们需要调用父类构造函数来初始化继承的属性。 子类通过使用super关键字来调用父类构造函数。super关键字既可以用来调用父类构造函数,也可以用来调用父类的方法。调用父类构造函数有两种方式:一是调用无参的构造函数,二是调用有参的构造函数。 如果父类定义了无参构造函数子类可以直接调用它来初始化继承的属性。例如: ``` public class Parent{ private int age; private String name; public Parent(){ age = 20; name = "ZhangSan"; } } public class Child extends Parent{ private String school; public Child(){ super(); // 调用父类的无参构造函数 school = "Tsinghua University"; } } ``` 如果父类定义了有参构造函数,就需要在子类构造函数中显示地调用它。例如: ``` public class Parent{ private int age; private String name; public Parent(int age,String name){ this.age = age; this.name = name; } } public class Child extends Parent{ private String school; public Child(int age,String name,String school){ super(age,name); // 调用父类的有参构造函数 this.school = school; } } ``` 在调用父类构造函数时,需要注意以下几点: 1. super()必须是子类构造函数中的第一行代码; 2. 如果子类调用父类构造函数是有参构造函数,也要确保父类有相应的构造函数; 3. 如果子类没有显示地调用父类构造函数,Java编译器会自动调用父类的无参构造函数。 总之,在子类构造函数调用父类构造函数是初始化继承属性的必要步骤,也是实现类之间继承的核心机制。对于Java的面向对象程序设计,这一点不容忽视。 ### 回答3: 在面向对象编程中,一个类可以作为另一个类的子类(也称为派生类)。子类可以继承父类的属性和方法,并且还可以添加自己的属性和方法。在子类构造函数中,可以通过调用父类构造函数来实现对父类属性的赋值。 在C++中,子类构造函数调用父类构造函数的方式是在子类构造函数初始化列表中调用父类构造函数。如下所示: ``` class Parent{ public: Parent(int a){ this->a = a; } private: int a; }; class Child: public Parent{ public: Child(int a, int b): Parent(a){ //调用父类构造函数 this->b = b; } private: int b; }; ``` 在上述代码中,Child是Parent的子类。当Child的构造函数调用时,会首先调用Parent的构造函数,初始化Parent类中的成员变量a,然后再初始化Child类中的成员变量b。 需要注意的是,在子类构造函数中,必须首先调用父类构造函数,因为子类继承了父类的属性和方法,需要确保这些属性和方法在子类对象被创建时已经得到正确的初始化。 除了初始化列表,还可以在子类构造函数中通过调用父类构造函数的方式来实现对父类属性的赋值,但是这种方式更加繁琐,且容易出错。因此,在子类构造函数中,最好采用初始化列表的方式调用父类构造函数进行赋值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值