java ctor_.ctor,.cctor 以及 对象的构造过程

article/2025/9/24 21:52:21

.ctor:

简述:构造函数,在类被实例化时,它会被自动调用。

当C#的类被编译后,在IL代码中会出现一个名为.ctor的方法,它就是我们的构造函数,对应C#中的构造函数。且看下面的代码:

731655954c7be9d8835ece551b5385f8.pngpublicclassClass1

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngprivatestringname;

1408c5260b2f05e450dee929db9be5f7.pngprivateintage;

f466905a3bcb5dcef110eab799825254.png}731655954c7be9d8835ece551b5385f8.png

类Class1中没有显示的构造函数,只有两字段,现在用ILDasm.exe打开编译后生成的exe文件,会看到:

5cee23c56caa394c80706d40a0822617.png

可以看到这里有个.ctor,我们没有定义构造函数,但这里却出现了.ctor,这就说明了:

当没有显示定义构造函数时,会自动生成一个构造函数,它没有参数,没有返回值。

那我们来看看这个.ctor都干了什么吧,双击.ctor打开,在弹出的窗口中可以找到下面的几行代码:

IL_0000: ldarg.0

IL_0001: call       instance void [mscorlib]System.Object::.ctor()

IL_0006: ret

上面就是这个.ctor的方法体,看上面的红色行,从字面上可以看出,它是调用(call)了一个类型为System.Object的实例的.ctor()方法,从这就可以证明:

当一个类没有显示声明继承于其它某个类时,它将默认继承自System.Object,并且,在类的构造函数中将会调用其基类的构造方法(.ctor)。

现在对上面的程序小改一下,在声明name时对其初始化:

731655954c7be9d8835ece551b5385f8.pngpublicclassClass1

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngprivatestringname="Lin";

1408c5260b2f05e450dee929db9be5f7.pngprivateintage;

f466905a3bcb5dcef110eab799825254.png}731655954c7be9d8835ece551b5385f8.png

再用ILDasm打开生成的exe文件,打开.ctor,里面有这么几行:

IL_0000: ldarg.0

IL_0001: ldstr      "Lin"

IL_0006: stfld      string ConsoleApplication1.Class1::name

IL_000b: ldarg.0

IL_000c: call       instance void [mscorlib]System.Object::.ctor()

IL_0011: nop

这个跟刚才的相比,多出了红色的那两行,这两行出现在“调用System.Object的构造方法”之前,这说明:

如果在字段声明的同时对其初始化,那么在编译后,赋值过程将被放到构造方法.ctor中,并且在调用其基类的构造方法之前进行。现在给上面的C#程序显式加上一个构造方法,它接受两个参数:

731655954c7be9d8835ece551b5385f8.pngpublicclassClass1

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngprivatestringname="Lin";

1408c5260b2f05e450dee929db9be5f7.pngprivateintage;

1408c5260b2f05e450dee929db9be5f7.png

1408c5260b2f05e450dee929db9be5f7.pngpublicClass1(stringname,intage)

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngthis.name=name;

1408c5260b2f05e450dee929db9be5f7.pngthis.age=age;

5bcb1807ee3e00d2b3c225f0b3f5c751.png        }f466905a3bcb5dcef110eab799825254.png}731655954c7be9d8835ece551b5385f8.png

再用ILDasm打开exe时,会发现有了点变化:

be344b4026109753d976ff0f1dd966e7.png

这里的.ctor带了两参数,一个string类型,一个int32类型,而刚才的无参无返回值的.ctor不见了,这也证明了:

如果类中有显式定义构造方法,那么就不会再自动生成一个无参数无返回值的默认构造方法。

打开.ctor,会看到其中有这么几行:

IL_0000:  ldarg.0

IL_0001: ldstr      "Lin"

IL_0006: stfld      string ConsoleApplication1.Class1::name

IL_000b: ldarg.0

IL_000c: call       instance void [mscorlib]System.Object::.ctor()

IL_0011: nop

IL_0012: nop

IL_0013: ldarg.0

IL_0014: ldarg.1

IL_0015: stfld      string ConsoleApplication1.Class1::name

IL_001a: ldarg.0

IL_001b: ldarg.2

IL_001c: stfld      int32 ConsoleApplication1.Class1::age

IL_0021: nop

从上面红色标识的代码的顺序中,我们可以进一步得到:

如果在声明字段时同时对其赋值,那么这个赋值过程将在类型的构造方法(.ctor)中最先执行,然后再执行其基类的构造方法,最后才轮到我们显示定义的构造方法体中代码。

.cctor

简述:类型初始化器,是一个静态方法,无参数无返回值,不能直接调用,最多只有一个

我们现在先给刚才的代码加上一个静态字段:

731655954c7be9d8835ece551b5385f8.pngpublicclassClass1

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngprivatestringname="Lin";

1408c5260b2f05e450dee929db9be5f7.pngpublicstaticintcount=50;

1408c5260b2f05e450dee929db9be5f7.pngprivateintage;

1408c5260b2f05e450dee929db9be5f7.png

1408c5260b2f05e450dee929db9be5f7.pngpublicClass1(stringname,intage)

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngthis.name=name;

1408c5260b2f05e450dee929db9be5f7.pngthis.age=age;

5bcb1807ee3e00d2b3c225f0b3f5c751.png        }f466905a3bcb5dcef110eab799825254.png}731655954c7be9d8835ece551b5385f8.png

再来打开ILDasm来看看:

5c82647017632cac5fd47612e135ffbd.png

发现这里多了一个.cctor,它就是类型初始化器,打开它,会看到其中有一句:

IL_0000: ldc.i4.s   50

IL_0002: stsfld     int32 ConsoleApplication1.Class1::count

它对静态字段count进行了赋值,值是50,那么,是.cctor先调用还是.ctor先调用呢?当然是.cctor,它是为初始化类型而生的,专搞静态的东东,而.ctor是构造方法,当然.cctor要先调用了。

现在显示加上一个.cctor,在C#中就是加个静态构造函数,我们不能为其指定访问修饰符(否则编译就会报错):

731655954c7be9d8835ece551b5385f8.pngpublicclassClass1

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngprivatestringname="Lin";

1408c5260b2f05e450dee929db9be5f7.pngpublicstaticintcount=50;

1408c5260b2f05e450dee929db9be5f7.pngprivateintage;

1408c5260b2f05e450dee929db9be5f7.png

1408c5260b2f05e450dee929db9be5f7.pngstaticClass1()

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.png            count=100;

5bcb1807ee3e00d2b3c225f0b3f5c751.png        }1408c5260b2f05e450dee929db9be5f7.png

1408c5260b2f05e450dee929db9be5f7.pngpublicClass1(stringname,intage)

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngthis.name=name;

1408c5260b2f05e450dee929db9be5f7.pngthis.age=age;

5bcb1807ee3e00d2b3c225f0b3f5c751.png        }f466905a3bcb5dcef110eab799825254.png}731655954c7be9d8835ece551b5385f8.png

再来看看现在ILDasm下的.cctor,其中有几行:

IL_0000: ldc.i4.s   50

IL_0002: stsfld     int32 ConsoleApplication1.Class1::count

IL_0007: nop

IL_0008: ldc.i4.s   100

IL_000a: stsfld     int32 ConsoleApplication1.Class1::count

可以看到:

如果在声明静态字段时同时对其赋值,它在编译后会被搬到.cctor中,并且是放在前面,然后才到显式定义的静态构造方法体中的代码,也就是说count在这里会被赋值两次,第一次50,第二次100。

在继承中对象构造过程

看下面这段程序:

731655954c7be9d8835ece551b5385f8.pngpublicclassA

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngpublicintx=1;

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.pngpublicA()222530190136c9c4cfd237cc0d5cff99.png{ m1(); }715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.pngpublicvoidm1()222530190136c9c4cfd237cc0d5cff99.png{ }f466905a3bcb5dcef110eab799825254.png    }731655954c7be9d8835ece551b5385f8.png

731655954c7be9d8835ece551b5385f8.pngpublicclassB : A

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngpublicinty=2;

1408c5260b2f05e450dee929db9be5f7.pngpublicstaticstringsb="B";

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.pngpublicB()222530190136c9c4cfd237cc0d5cff99.png{ m2(); }715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.pngpublicvoidm2()222530190136c9c4cfd237cc0d5cff99.png{ }f466905a3bcb5dcef110eab799825254.png    }731655954c7be9d8835ece551b5385f8.png

731655954c7be9d8835ece551b5385f8.pngpublicclassC : B

24a924a57ba6b3f2b51fc9edb7ea4186.png

9310e85a14af99de4811ff4c77f1f911.png222530190136c9c4cfd237cc0d5cff99.png{

1408c5260b2f05e450dee929db9be5f7.pngpublicintz=3;

1408c5260b2f05e450dee929db9be5f7.pngpublicstaticstringsc="C";

715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.pngpublicC()222530190136c9c4cfd237cc0d5cff99.png{ m3(); }715f2d05503b99d41f3b6ba2cdccc84d.png

e083dfde5a91f50979fe8979b4012b9d.pngpublicvoidm3()222530190136c9c4cfd237cc0d5cff99.png{ }f466905a3bcb5dcef110eab799825254.png    }731655954c7be9d8835ece551b5385f8.png

编译后用ILDasm打开生成的exe文件:

12dcef0f5609fc4aa4fc1266c2ac1cff.png

可以看到三者都有一个.ctor,B、C中有.cctor,而A没有,打开B,C的.cctor,可以看到它们都负责初始化自己的静态字段,现在主要来看它们的.ctor。

先看类C的.ctor:

IL_0001: ldc.i4.3

IL_0002: stfld      int32 ConsoleApplication1.C::z

IL_0007: ldarg.0

IL_0008: call       instance void ConsoleApplication1.B::.ctor()

IL_000d: nop

IL_000e: nop

IL_000f: ldarg.0

IL_0010: call       instance void ConsoleApplication1.C::m3()

可以看到:

在C被实例化时,它最先初始化在声明时同时赋值的字段(非静态),此处是将3赋给z,然后它会调用其基类的.ctor(),然后再调用自己的实例方法m3(),值得注意的是,在执行显式定义的构造方法体中的代码前,会先调用其基类的构造方法(创建基类的实例)。再来看类B的.ctor():

IL_0001: ldc.i4.2

IL_0002: stfld      int32 ConsoleApplication1.B::y

IL_0007: ldarg.0

IL_0008: call       instance void ConsoleApplication1.A::.ctor()

IL_000d: nop

IL_000e: nop

IL_000f: ldarg.0

IL_0010: call       instance void ConsoleApplication1.B::m2()

同样,我们可以看到,在实例化B时,它会先把2赋给自己的y,然后再调用基类A的构造方法,最后再调用自己的实例方法m2()。

那A的.ctor()就不再看了,可以猜到它一定是在做这样的事:

1、 将1赋给实例的x字段;

2、 调用基类System.Object的构造方法.ctor来创建基类的实例;

3、 调用实例方法m1();

总结

1、.ctor是构造方法;

2、.cctor是类型初始化器,在C#中也就是静态构造函数;

3、当类C实例化时,会先对声明时就进行赋值的字段赋值,然后调用基类的构造函数,基类再以同样的方法构造自己,一直到顶层的System.Object,然后再回来执行C的显式构造方法中的代码,就是这么一个递归的过程。

参考资料

1、《Essential .NET》 Volume 1


http://chatgpt.dhexx.cn/article/KBMxcRe6.shtml

相关文章

Hive基础sql语法

原文:https://www.cnblogs.com/HondaHsu/p/4346354.html 1.先建 表 drop table sfmk.xzz_0726_srcarea_desarea; create table sfmk.xzz_0726_srcarea_desarea ( srcarea_desarea string ) row format delimited fields terminated by ,;2.将数据上传至hive 服务器 如&#xf…

内容耦合 c语言例子,耦合性 内聚加实例

基本信息 耦合性(或称"耦合度") 英文 : coupling 耦合性也叫块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块之间越独立则越差,模块间耦合的高低取决于模块间接口的复杂性,调用的方式以及传递的信息。 形…

[架构之路-183]-《软考-系统分析师》-13-系统设计 - 高内聚低耦合详解、图解以及技术手段

目录 第1章 什么是高内聚低耦合 1.1 概念 1.2 目的 1.3 什么时候需要进行高内聚低耦合 1.4 什么系统需要关注高内聚、低耦合 第2章 分类 2.1 内聚的分类 2.2 耦合的分类 第3章 增加高内聚降低耦合度的方法 3.1 增加高内聚 3.2 降低耦合度 第1章 什么是高内聚低耦…

java 高内聚低耦合_关于高内聚低耦合概念的理解

概念 高内聚低耦合,是软件工程中的概念,是判断软件设计好坏的标准,主要用于程序的面向对象的设计,主要看类的内聚性是否高,耦合度是否低。 目的 使程序模块的可重用性、移植性大大增强。 通常程序结构中各模块的内聚程…

lea 指令的小解

LEA指令的功能是将源操作数、即存储单元的有效地址(偏移地址)传送到目的操作数。 官方8086的白皮书,我的英语水平有限,只找到如下内容; ^offset和 Lea 伪指令 ; offset 此为一个伪指令,它不会有…

X86 LEA指令详解

1.SDM指令功能描述(LEA) LEA 总体描述: 从第二个操作数(源操作数)计算有效地址,并将结果存入第一个操作数(目的操作数)。源操作数是指定了一种访存操作的内存地址,目的操作数为一个通用寄存器…

css3围绕旋转动画js特效

下载地址 css3围绕旋转动画特效,两种旋转方式,自转和公转,css3按照固定的轨迹行驶。 dd:

HTML5 - 用CSS3动画制作场景切换效果(移动,旋转,淡入淡出等)

两个场景(即两个div视图)切换的时候,如果想添加个过渡动画,除了可以使用js来实现,还可以通过CSS3的animation属性来实现。 (注意:Internet Explorer 9 以及更早的版本不支持 animation 属性。&…

CSS3 元素转圈动画 (元素旋转动画)

CSS3 元素转圈动画 &#xff08;元素旋转动画&#xff09; <!DOCTYPE html> <html> <head> <style> div { width:100px; height:100px; background:red; position:relative; animation:mymove 5s infinite; -webkit-animation:mymove 5s infinite; /*…

css3 animation动画360旋转。旋转效果用transform:rotate过渡。

旋转&#xff1a; <div class"lds-hourglass"></div> .lds-hourglass {display:inline-block;position:relative;width:72px;height:72px } .lds-hourglass:after {content:" ";display:block;border-radius:50%;width:0;height:0;margin:6p…

css的rotate3d实现炫酷的圆环转动动画

1.实现效果 2.实现原理 2.1 rotate3d rotate3d&#xff1a;rotate3d() CSS 函数定义一个变换&#xff0c;它将元素围绕固定轴移动而不使其变形。运动量由指定的角度定义; 如果为正&#xff0c;运动将为顺时针&#xff0c;如果为负&#xff0c;则为逆时针。 语法&#xff1a; …

CSS3实现3d图片旋转动画效果

1.实现原理 1.首先所有的图片的容器position:absolute&#xff0c;叠加在一起&#xff0c;然后一次设置rotateY分别为&#xff08;36*i&#xff09;deg &#xff0c;i取0到10 &#xff1b;所有图片会相交成一个类似花的形状 然后为每个图片的容器设置translateZ&#xff0c;所有…

CSS3实现立体旋转动画效果

问题描述 一个3D立体图形在网页中呈现旋转效果,看起来十分炫酷,这个过程的实现其实并不困难,只需要利用CSS3动画效果,并对属性设置合适的参数之后,效果就能实现了。 在解决问题之前,需要先了解CSS3的动画效果是什么,其相关属性和参数有什么含义,这个过程就请查看CSS3动…

css3d动画:平移、旋转、缩放

1、前言&#xff1a; 3d比2d多了一个z轴&#xff0c;这个z轴是垂直我们屏幕的方向&#xff0c;指向我们人眼的是正轴&#xff0c;远离人眼的是负轴&#xff0c;图示如下&#xff1a; 2、景深设置和3d平移 当我们在z轴上向正轴方向上移动物体&#xff0c;也就是向我们眼前移动…

使用css3属性做一个循环旋转的动画

做这个动画是使用css3中的animation 和 @-webkit-keyframes 组合使用来完成 //这是html部分代码 <div class="home"><imgalt="logo"src="http://b-ssl.duitang.com/uploads/item/201206/26/20120626190359_MjB3s.jpeg"id="images…

基于HTML5+CSS3的图片旋转、无限滚动、文字跳动特效

本文分享几种基于HTML5CSS3实现的一些动画特效&#xff1a;图片旋转、无限滚动、文字跳动;实现起来均比较容易&#xff0c;动手来试试! 一、图片旋转 效果图如下&#xff1a; 这个效果实现起来其实并不困难。代码清单如下: <style type"text/css"> #liu{ wid…

内存碎片产生原因及终极解决办法

内存碎片通常分为内部碎片和外部碎片&#xff1a; 1. 内部碎片是由于采用固定大小的内存分区&#xff0c;当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片&#xff0c;通常内部碎片难以完全避免&#xff1b; 2. 外部碎片是由于某些未分配的连续内存区域太小&…

6、Redis内存碎片优化

1、Redis内存碎片是如何形成的 一是内存分配器的分配策略&#xff0c;内存分配器一般是按固定大小来分配内存&#xff0c;而不是按实际使用大小来分配。例如8字节、16字节、32字节...&#xff0c;2KB&#xff0c;4KB&#xff0c;8KB等&#xff0c;按程序申请时最接近某个固定值…

内存碎片处理技术

内存碎片处理技术 内存碎片是一个很棘手的问题。如何分配内存决定着内存碎片是否会、何时会、如何会成为一个问题。 即使在系统中事实上仍然有许多空闲内存时&#xff0c;内存碎片还会最终导致出现内存用完的 情况。一个不断产生内存碎片的系统&#xff0c;不管产生的内存碎片…

内存碎片产生原因及处理

内存碎片是一个很棘手的问题。如何分配内存决定着内存碎片是否会、何时会、如何会成为一个问题。  即使在系统中事实上仍然有许多空闲内存时&#xff0c;内存碎片还会最终导致出现内存用完的情况。一个不断产生内存碎片的系统&#xff0c;不管产生的内存碎片多么小&#xff0…