构造函数你真的看懂了吗

转载 2016年08月28日 15:37:53

转载自:http://www.cnblogs.com/CreateMyself/p/4730049.html

看过我之前复习的随笔知道都是基础之上的语法,但是当我脑海开启回忆基础知识时,尤其是构造函数中先后执行顺序以及原因却是模棱两可,于是开始边编写边操笔来记叙下来。如果你正在学习基础语法或者是复习基础语法的路上,这篇文章或许对你亦有帮助(当然msdn也有相关定义,但是个人觉得要是看完定义后再去摸索下,或许会理解的更透彻吧)。【特此注意:高手请绕过道而走!】

继承之构造函数

 首先我们定义一个Person类,并给个构造函数。代码如下:

复制代码
    public class Person
    {

        public string Name { get; set; }

        public int Age { get; set; }

        public bool Gender { get; set; }

        public Person()
        {
            Console.WriteLine("父类构造函数");
        }
    }
复制代码

再写个Bob类继承该父类,同时给个构造函数,其代码如下:

复制代码
    public class Bob : Person
    {
        public Bob()
        {
            Console.WriteLine("子类构造函数");
        }
    }
复制代码

接下来就在控制台实例化Bob类  Bob b = new Bob(); 看调用构造函数先后执行顺序。结果输出如下:

从这输出来看你会不会妄下结论说先调用的父类构造函数再调用子类的构造函数呢?如果你这样说的话,也就是说当我们实例化对象子类时,但是它去执行了父类的构造函数,好像有点神奇。好吧,我们接下来继续看,我们再定义一个Student类继承该Bob类。代码如下:

复制代码
    public class Student : Bob {

        public Student() {
            Console.WriteLine("学生类构造函数");
        }
    }
复制代码

然后在控制台实例化Student类 Student s = new Student(); ,结果打印出:

依然是调用的父类构造函数,所以现在就下结论:子类继承父类,实例化子类对象,首先调用的是父类构造函数。好,结论似乎言之过早,我们来断点调试下不就可以得出答案所在了吗。 看下面图片,一步步断点截图:

第一步:不用说

第二步:调用子类构造函数即Bob类

第三步:调用父类构造函数即Person类

第四步:执行父类的构造函数

最后一步:执行子类构造函数

所以总结如下:子类继承父类时构造函数执行的先后顺序:

  1. 调用子类构造函数
  2. 调用父类构造函数
  3. 执行父类构造函数
  4. 执行子类构造函数 

 但是此时问题来了,为什么我们实例化子类对象时,最后去调用了父类的构造函数?其实是有道理可循的,我忘记了base关键字的存在,当你在子类编写构造函数时,如果没有显式的调用基类的构造函数,那么会默认在其后面添加 一个 :base() 以此来调用基类的无参构造函数。所以上述问题就解决了。

msdn上所说base关键字概念有两点:

  • 调用基类上已被其他方法重写的方法。

  • 指定创建派生类实例时应调用的基类构造函数。

接下来如果我们在父类中将无参构造函数修改为有参构造函数会怎样呢,这是我们再生成出现如下错误:

因为上面已经说的很清楚了,如果父类没有无参构造函数,但是子类默认会带调用父类无参的构造函数所以会出错。所以解决办法 就是在子类中构造函数显示的用 :base 来调用父类有参构造函数即可,具体不再演示。 

但是问题又来了:为什么要让父类构造函数优先于子类构造函数执行呢??????请看下面给出合理解释。

我们将整个代码进行整合如下来讨论下:

复制代码
    public class Person
    {

        public string Name { get; set; }

        public int Age { get; set; }

        public bool Gender { get; set; }

        public Person(string name,int age)
        {
            Console.WriteLine("父类构造函数");
        }
    }

    public class Bob : Person
    {
        public Bob(string name,int age):base(name,11)  (1)
        {
            this.Name = name;
            this.Age = age;
            Console.WriteLine("子类构造函数");
        }
    }

    
    class Program
    {
        static void Main(string[] args)
        {

            Bob b = new Bob("1",12); (2)
            Console.ReadKey();
        }
    }
复制代码

我们只需看上述代码中标记为红色的(1)和(2),如果我们现在实例化Bob类并传参 age = 12 ,此时我们当然希望的是Bob类中age = 12,如果此时先执行子类那完了,因为有 :base(name,11) ,所以此时的 age = 11 ,这样不就造成了意想不到的结果了吗,明明传的12,结果为11,结果数据产生严重的冲突,也就是数据的不一致。如果先执行父类构造函数,此时子类age为11,但是当执行到子类构造函数时,因为我传的是12所以其age就为12。这才是我们需要的结果。

静态构造函数 

 我们继续就上述例子中的Person类为例进行分析,代码如下:

复制代码
    public class Person
    {

        public string Name { get; set; }

        public int Age { get; set; }

        public bool Gender { get; set; }

        public Person()
        {
            Console.WriteLine("实例构造函数");
        }

        static Person()
        {
            Console.WriteLine("静态构造函数");
        }
    }
复制代码

 为了更好演示,我们实例化对象两次代码如下:

     Person p = new Person();
     
Person p1
= new Person();

控制台运行结果如下:

从上述显示结果中至少可以看出:在调用构造函数之前就已经调用了静态构造函数。 那么我们比调用构造函数更早的时期是什么时候呢?啊,容我想想,直接声明该对象的变量(即直接第一次在加载该类下的所有成员时),不实例化就可以了。于是乎我们直接在控制台中声明变量即可,如下:

 Person p2;

运行,结果是什么都没有如下:

说明此时未调用静态构造函数, 那就是在此之后,现在是调用实例构造函数之前即对象不能实例化,也就是说访问实例成员指定也是不行了,那么要是如果访问静态成员,看行不行,我们在Person类中添加 

public static int age;

现在我们在控制台来访问该静态成员 Person.age = 1; 结果运行如下:

说明静态构造函数: 在类的成员第一次被访问之前,就会调用静态构造函数 。 如果还是不能理解我们直接这样做,在Person类中定义一个已经赋值的字段。如下

  public int temp = 0;

此时我们在控制台中实例化对象 Person p = new Person(); ,再运行下看看结果会怎样呢?一步步调试:

第一步:显然没有调用

第二步执行到赋值的字段时:

同样也证明了上面的结果。

补充

如果对静态成员不太理解下面就静态成员定义以及静态成员和实例成员区别做一点概括吧。

 静态成员

静态成员在类第一次加载时被创建。

静态成员只会被创建一次,所以有且仅有一份。

静态成员创建在静态存储区中,所以一旦被创建直到程序退出才会被回收。

静态成员与实例成员区别

生命周期:静态成员是从类第一次被加载时到程序完全退出时,但是实例成员则是在对象被创建时到该对象成为垃圾被垃圾回收器回收时。

存储位置:静态成员存储在静态存储区中,实例成员存储在堆空间相应的对象中。

相关文章推荐

《大话西游》你真的看懂了吗?

前天晚上去看了《大话西游之大圣娶亲》,虽然是一部重映电影,片方又新加了11分钟新的剧情,但是每次看这部经典之作都有不同的感想。 来看一部20年经久不衰的电影《大话西游》,1994年我们看它曾经捧...
  • CC1991_
  • CC1991_
  • 2017年04月24日 16:19
  • 640

《大话西游》20年后重映(附影评:《大话西游》你真的看懂了吗?)

“电影里的台词几乎都能背,但在影院里再看还是会笑,看到最后紫霞仙子死的时候,还是忍不住落泪!”昨天下午,经典老片《大话西游》在海航活力天宝影城重新上映,一位重温影片的观众如是说。还有观众反映,用现在的...
  • chnechen
  • chnechen
  • 2014年10月27日 11:25
  • 172257

你真的看懂R中的stem函数了吗?

哭晕,看到书上的介绍,你一定觉得你已经学会了如何使用R中的stem()函数绘制茎叶图了。 stem()函数的使用方法是: stem(x, scale=1,width=80, ato...

一张图看懂MingW GCC 5.2.0中的C++的的拷贝构造函数

要了解copy构造函数使用原理,就好是用完成的代码测试一下,看看它是怎么工作,在什么 情况下会被调用,什么 情况下不会被调用,百度了一些名师讲座,在这里讲的也是乱七八糟,与我在实际环境中,实验结果,也...
  • vs9841
  • vs9841
  • 2015年10月24日 16:58
  • 543

Sencha Touch 快速入门2.0 第四章 Api文档看懂了吗?

今天来扯一扯官方文档以及其他的一些东西。大部分问题,其实都是可以通过查阅文档来解决的。   因为文档是没办法直接用浏览器打开来看的,所以请大家先将sencha-touch-1.1.0下的docs...

用例图——你真的懂了吗?

一般我们介绍一个事物的时候,都会从是什么、为什么、怎么样三个角度来阐述问题。对于我们用例图来说,除了这三个方面,还要加上一个何时画得问题。然而,这些知识都是比较基础的东西,网上的资料比比皆是,本文就不...

关于自增自减,你真的懂了吗?

int x =5 ;  printf("%d\n",x++);  printf("%d\n",++x);int x =5; printf("%d %d %d\n",++x,++x,++x);int x...

unsigned 关键字,你真的懂了吗?

前不久和同事谈论起 unsigned 关键字,故今天小结一下。 以32位机为例,int 分为无符号 unsigned 和有符号 signed 两种类型,默认为signed。二者的区别就是无符号类型能保...

c++的overload override overwrite 你真的懂了吗

以下是对C++中overload,override,overwrite的区别进行了详细的分析介绍,需要的朋友可以过来参考下Overload(重载):在C++程序中,可以将语义、功能相似的几个函数用同一...

Java的单例模式你真的懂了吗?

原文出处: 张新强 1. 前言 单例(Singleton)应该是开发者们最熟悉的设计模式了,并且好像也是最容易实现的——基本上每个开发者都能够随手写出——但是,真的是这样吗? 作为一个Java开发...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:构造函数你真的看懂了吗
举报原因:
原因补充:

(最多只允许输入30个字)