关于子类对象的构造函数和父类构造函数的执行顺序

我们分别为父类和子类添加显式的构造函数,代码如下:


class Person
    {
        private int nAge;
        protected string strName;
        double douHeight;
        public string strEateType;
        //父类的构造函数
        public Person()
        {
            Console.WriteLine("我是父类的构造函数");
        }
        public void Hello()
        {
            Console.WriteLine("我可以说Hello!");
        }
        public void Run()
        {
            Console.WriteLine("我可以跑!");
        }
    }
    class Student : Person
    {
        private string strClass;
        private string strAddress;
        //子类的构造函数
        public Student ()
        {
            Console.WriteLine("我是子类的构造函数");
        }
    }
 

我们使用VS的单步调试,来看父类和子类显式构造函数的执行顺序,如下图(动态图片,可以看到过程):

很容易的可以发现,当创建子类对象的时候
①先调用了子类的构造函数
②调用了父类的构造函数
③执行了父类的构造函数
④执行了子类的构造函数

那么为什么会这样呢?
我尝试通过反编译看源码来解释这个原因,但是反编译的结果如下,

没有发现有什么特别的地方可以解释这个原因。

最后还是查阅微软的MSDN官方文档找到了答案(原文地址点击这里

根据微软官方的代码示例,那么下面的代码的效果也是相同的

//子类的构造函数
02
        public Student ()
03
        {
04
            Console.WriteLine("我是子类的构造函数");
05
        }
06
//这里的代码和上面的代码效果是相同的
07
        public Student()
08
            :base()
09
        {
10
            Console.WriteLine("我是子类的构造函数");
11
        }

也就是说只要在子类显式的声明了无参的构造函数,在实例化子类的对象是,子类的无参构造函数都会去调用父类无参的构造函数。
那么,如果父类没有这个无参的构造函数则会报错。
如下面的代码:

class Person
02
    {
03
        private int nAge;
04
        protected string strName;
05
        double douHeight;
06
        public string strEateType;
07
        //父类的构造函数
08
        //public Person()
09
        //{
10
        //    Console.WriteLine("我是父类的构造函数");
11
        //}
12
      //父类的有参数的构造函数,这里覆盖了无参的构造函数
13
        public Person (string str)
14
        {
15
            Console.WriteLine("我是父类的构造函数{0}",str);
16
        }
17
        public void Hello()
18
        {
19
            Console.WriteLine("我可以说Hello!");
20
        }
21
        public void Run()
22
        {
23
            Console.WriteLine("我可以跑!");
24
        }
25
    }
26
    class Student : Person
27
    {
28
        private string strClass;
29
        private string strAddress;
30
        //子类的无参构造函数
31
        public Student ()
32
        {
33
            Console.WriteLine("我是子类的构造函数");
34
        }
35
        public Student(string strName)
36
        {
37
            Console.WriteLine("我的名字叫{0}",strName);
38
        }
39
    }

这时候编译会报错,

因为在父类中有参数的构造函数覆盖了无参数的构造函数,所以在子类的无参数的构造函数没办法回调父类的无参数的构造函数初始化父类的成员变量。所以报错。

那么在初始化子类的时候,为什么要调用父类的构造函数呢?
在初始化子类之前需要通过构造函数初始化父类的成员变量
父类的构造函数先于子类的构造函数执行的意义是什么呢?
当在父类的构造函数中和子类的构造函数中为父类的非私有成员变量赋不同默认值。当实例化子类,子类要调用构造函数初始化成员变量,如果先执行了子类的构造函数,再执行父类的构造函数,父类成员字段的值会覆盖子类成员字段的值。但是我们想得到的是子类的属性值。所以为了解决数据冲突,父类的构造函数要先于子类的构造函数执行。
如下面的代码:

class Person
02
    {
03
        private int nAge;
04
        private string strName;
05
        double douHeight;
06
        public string strEateType;
07
       // 父类的构造函数
08
        public Person()
09
        {
10
            //再父类中对strEateType赋初始值
11
            this.strEateType = "吃饭";
12
            Console.WriteLine("我是父类的构造函数{0}", strEateType);
13
        }
14
    }
15
    class Student : Person
16
    {
17
        private string strClass;
18
        private string strAddress;
19
        //子类的构造函数
20
        public Student()
21
        {
22
            //在子类中对strEateType赋初始值
23
            this.strEateType = "吃面条";
24
            Console.WriteLine("我是子类的构造函数{0}",strEateType);
25
        }
26
    }

这时候我们通过,声明子类对象访问strEateType的值,如下:

Student stu1 = new Student();
2
            //stu1.
3
            string str = stu1.strEateType.ToString();
4
            Console.WriteLine(str);
5
            Console.ReadKey();

这里肯定是要打印出子类的属性strEateType的值,如果先执行子类构造函数对strEateType赋值,然后父类的构造函数赋值覆盖strEateType的初始值。那么打印出的将是父类成员字段的值。所以,父类的构造函数先于子类的构造函数执行。
打印结果如下:

原文链接:关于子类对象的构造函数和父类构造函数的执行顺序

时间: 2024-12-29 16:34:57

关于子类对象的构造函数和父类构造函数的执行顺序的相关文章

详解~实现Runnable方法创建线程之为什么要将Runnable接口的子类对象传递给Thread的构造函数

/** * @author zhao * @TIME 0419 22:56  End *定义线程的第二种方法:实现Runnable接口 *步骤:1,定义一个子类实现Runnable接口 *    2,在子类中覆盖run()方法,并且将多线程锁执行的代码写入run方法中 *    3,通过Thread类建立线程对象: *    4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数.  为什么要将Runnable接口的子类对象传递给Thread的构造函数.  因为,自定义的

子父类中码静态代块 构造代码块. 代码块 构造函数 成员变量 实例变量 执行顺序

刚开始接触时,很不容易分清楚 创建子类或者多态的情况: /* 创建子类的情况: 1.父类静态代码块 2.子类静态代码块 3.父类构造块 /实例变量(new 创建的变量成员)//谁在前执行谁,执行完再执行构造函数 4.父类构造函数//父类构造函数有方法,如果方法子类有就执行子类的方法,没有才再执行父类方法.//如果子类有父类没有会报错!//父类私有该方法就执行父类的方法 5.子类构造块/实例变量//谁在前执行谁,执行完再执行构造函数 6.子类构造函数 7.子类普通方法//调用成员变量,看子类的值,

JavaSE8基础 不同包下的子类中,创建子类对象可以继承到父类的 受保护/公有方法

os :windows7 x64    jdk:jdk-8u131-windows-x64    ide:Eclipse Oxygen Release (4.7.0)        代码: 父类: package jizuiku; public class Person { void showDefault() { System.out.println("showDefault"); } private void showPrivate() { System.out.println(&

java新建对象的static块与构造器的执行顺序

前言:本文解决的问题 新建一个对象静态代码块什么时候执行 {}里面的代码什么时候执行 有继承关系时的执行顺序 1.问题出现的背景: 构造器是用来实例化一个对象,当我们使用new关键字来新建对象时,构造器就会被调用.如果class中含有静态代码块(static)和普通代码块(在{}括号下),新建对象时的调用顺序是:静态代码块>{里面的代码}>构造器. 2.例子说明: 2.1代码说明 //父类 public class StaticExample{ { System.out.println(&qu

Java类静态属性、静态块、非静态属性、非静态块、构造函数在初始化时的执行顺序

前言 今天在看Android ContentProvider实现的时候,突然想到了Java类在new的过程中,静态域.静态块.非静态域.非静态块.构造函数的执行顺序问题.其实这是一个很经典的问题,非常考察对Java基础知识的掌握程度.很多面试过程中相信也有这样的问题,趁着周末有时间复习一下. 结论 这里先把整理好的结论抛给大家,然后我在写个程序来验证我们的结论.在Java类被new的过程中,执行顺序如下: 实现自身的静态属性和静态代码块.(根据代码出现的顺序决定谁先执行) 实现自身的非静态属性和

Java中子类和父类相关方法的执行顺序

无意中看到下面一个题目,大家一起来看看最后的输出结果是什么.反正我看完之后,用IDE测试后感觉知识点得到巩固了. 1 /** 2 * 函数执行顺序测试 3 * Created by 萌小Q on 2017/5/17 0017. 4 */ 5 public class ExeSeqTest { 6 7 public static void main(String [] args){ 8 System.out.println(new B().getValue()); 9 } 10 static cl

c++ 子类要正确的调用父类构造函数

class base{ public: int i,j; base(){ i=j=0; } base(int a,int b){ i=a;j=b; } }; class deried:public base{ public: deried(int a,int b){ base(a,b); } }; int main(){ deried d(3,1); cout<<d.i<<" "<<d.j<<endl; return 0; } 上面的这段

Scala 中 构造函数,重载函数的执行顺序

在调试scala在线开发教程(http://www.imobilebbs.com/wordpress/archives/4911)的过程中看到了以下代码,但是这段代码无论怎么调试都无法成功. 1 abstract class Element{ 2 def contents:Array[String] 3 val height:Int = contents.length 4 val width:Int = if(height==0) 0 else contents(0).length 5 } 6

多继承父类构造方法的执行顺序

1 class a: 2 def __init__(self): 3 print('a') 4 5 class b(a): 6 def __init__(self): 7 super().__init__() 8 print('b') 9 10 class c(a): 11 def __init__(self): 12 super().__init__() 13 print('c') 14 15 16 class d(b,c): 17 pass 18 19 mm=d() 20 ''' 21 a