在上篇中留下了一个问题,想来有心的读者已经自行解决问题并且得出了自己的结论。事实上,程序并不是象通常的函数调用,进进入主调函数,然后进入被调函数。在这里,通过单步执行,可以看到程序先进入到类A中进行静态成员的初始化,然后进入到类B中进行B的静态成员的赋值,最后才进入Main函数。我们可以猜测,编译器根据Main函数中所用到的类的先后顺序对类的静态成员进行初始化。在我们的例子中,编译器检查到引用了类B的静态成员,接着继续检查该成员又用到了类A的静态成员,经过优化,编译器先初始化了类A的静态成员,然后是类B,然后回到主函数继续来执行,这符合我们看到的单步执行的顺序。
然而,在有构造函数的情况下,事情又非如此。还是刚才的例子,我们把类A的构造函数改为静态,代码如下:
- 1 class A
- 2 {
-
3 public static int num4 = 1;
-
4 static A()
- 5 {
- 6 }
- 7 }
- 8
-
9 class B
- 10 {
-
11 public static int num5 = A.num4+1;
-
12 B(){}
- 13 }
通过单步执行,我们可以看到,程序先进入到了类B,在对B的静态成员求值的时候才进入了类A,最后进入Main函数。现在,我们再做一点改动,把类B的构造函数也改为静态的:
- 1 class Program
- 2 {
-
3 public static int num1;
-
4 public static int num2 = 1;
-
5 public static int num3;
-
6 static void Main(string[] args)
- 7 {
- 8 Console.WriteLine(num2);
-
9 Console.WriteLine(B.num5);
- 10 Console.ReadLine();
- 11 }
-
12 static Program()
- 13 {
- 14 Console.WriteLine(num1);
- 15 num3++;
- 16 Console.WriteLine(num3);
- 17 }
- 18 }
- 19
-
20 class A
- 21 {
-
22 public static int num4 = 1;
-
23 static A()
- 24 {
- 25 }
- 26 }
- 27
-
28 class B
- 29 {
-
30 public static int num5 = A.num4+1;
-
31 static B(){}
- 32 }
通过单步执行,我们可以看到会先进入到Main函数中,然后进入到类B,然后是类A。
现在,我们可以做一个最终猜测了:编译器在编译的时候,会事先分析所需要的静态字段,如果这些静态字段所在的类有静态的构造函数,则忽略字段的初始化,否则先进行静态字段的初始化。对类的静态成员初始化的顺序取决于在Main函数中的引用顺序,先引用到的先进行初始化,但如果类的静态成员的初始化依赖于其它类的静态成员,则会先初始化被依赖类的静态成员。而带有静态构造函数的类的静态字段,只有在引用到的时候才进行初始化。
回过头来考虑最初令人有些迷惑的代码,可以发现已经不再难以理解。第一段代码中A和B都是具有静态构造函数的类,所以是从Main函数中引用到类A的时候开始进入到类A进行初始化,在初始化的过程中又用到了B的静态成员,然后进入到B中。对于第二段代码,由于B是非静态的构造函数,所以会在主函数执行前被初始化,同样由于初始化的过程中用到了类A的静态成员,然后跳转到类A。这样就造成了代码一和代码二运行之后得到不同的结果。
附:文中所用代码仅为示例之用,并不符合良好代码的规范和要求。尤其使用未经赋值的字段是一个非常不好的编程习惯,请阅读此文的人予以注意。
分享到:
相关推荐
java 静态_非静态 字段_方法_代码块 子类父类构造_初始化顺序! 三个class 让你清清楚楚 第一个class java代码如下: package initialOrder; class Parent { // 静态变量 public static String p_StaticField...
它们可以被视为构造函数的补充,提供额外的初始化逻辑,特别是当多个构造函数需要执行相同的初始化操作时。 ```java public class InitFiledBlockStatic { int instanceVar; { // 对象初始化块中的代码 ...
3. **静态构造函数**:静态构造函数在类首次被引用时执行一次,可以用于初始化静态字段。静态构造函数不能被直接调用,由编译器自动处理。 在`eg3_10`这个示例文件中,可能包含了一个或多个类,其中一个类定义了...
- 静态构造函数与实例构造函数并存,两者执行时机不同:静态构造函数在类加载时执行,实例构造函数在创建实例时执行,所以它们之间不会冲突。 3. 构造函数初始化器: - 当一个构造函数需要基于另一个构造函数进行...
静态构造函数主要用于初始化类的静态字段,确保在类的生命周期内只进行一次初始化。 反射API则提供了在运行时动态地获取和使用类型信息的能力。例如,`System.Reflection.Assembly`类用于加载和处理程序集,`System...
2. 非静态数据成员:在构造函数初始化列表中,成员变量按照它们在类声明中的顺序被初始化。即使初始化列表中的顺序不同,也会按照声明的顺序进行。 例如: ```cpp class MyClass { public: int a; int b; static...
在.NET开发中,如果你需要在静态成员中实现线程安全,通常推荐使用静态初始化器(例如,`static readonly`字段的初始化)或者在首次访问静态成员时进行同步,而不是在静态构造函数中。这种方式可以避免潜在的死锁...
7. **构造函数与static字段**:静态字段属于类,不属于类的任何实例,因此不应在构造函数中初始化。它们通常在静态初始化块`{}`中初始化,或者在类加载时通过`static`关键字直接赋值。 8. **构造函数与访问修饰符**...
### Java静态数据初始化详解 #### 一、Java静态初始化...通过本篇文章的学习,我们了解了Java中静态初始化的基本概念、执行过程以及如何正确地在程序中使用静态初始化。这对于理解和优化Java应用程序具有重要意义。
静态构造函数通常用来设置类型级别的初始化,例如初始化静态字段或者创建静态资源。 再来看静态方法和实例方法。静态方法属于类,可以直接通过类名调用,不需要实例化对象。它们不能访问类的非静态成员,因为这些...
构造函数具有与类相同的名称,它通常初始化新对象的数据成员。在C#中,构造函数可以有多个,但它们的方法签名不能相同。 默认构造函数是无参数的构造函数。无论何时,只要使用new运算符实例化对象,并且不为new提供...
初始化的顺序是:首先是静态初始化块,然后是父类的非静态初始化,接着是子类的非静态初始化,最后是构造函数中的初始化代码。在`Test.java`中,可能展示了这种初始化顺序。 总结,这个压缩包文件提供了关于Java中...
静态构造函数用于对静态字段、只读字段等的初始化。静态构造函数添加 static 关键字,不能添加访问修饰符,因为静态构造函数都是私有的。类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用...
在示例代码中,`test.b` 就是一个只读字段,它可以在静态构造函数中进行初始化,如: ```csharp public static readonly int b; ``` 在静态构造函数中,我们为 `b` 赋值: ```csharp static test() { b = 2; } `...
- 构造函数的执行顺序大致为:先分配静态成员的内存空间,然后执行静态成员初始化,接着执行静态构造函数;之后分配对象实例的内存空间,执行实例成员初始化,最后执行实例构造函数。 #### 三、静态成员与实例成员...
每个类可以有一个默认构造函数(无参数的构造器),也可以根据需求定义带参数的构造函数。构造方法的主要作用包括: 1. 初始化类的成员变量。 2. 执行必要的设置工作,比如资源分配。 3. 链接到其他构造函数,以实现...
最后,我们还会讨论C#中的构造函数初始化器,它允许我们在类的字段声明处指定初始化值,简化了代码并提高了可读性。 总的来说,ACCP6.0-二期的构造函数教程旨在帮助开发者掌握.NET环境下构造函数的使用,提升代码...
类的基本特性包括构造函数和拷贝构造函数,其中构造函数用于初始化新创建的对象,而拷贝构造函数则用于复制对象。静态成员是属于类而非类的特定实例的,它们可以被所有对象共享,且可以被静态成员函数访问。友元机制...
4. **静态字段初始化**:静态字段的初始化直接写在字段声明后的赋值语句会在静态构造函数之前执行。这是静态字段的预初始化,然后由静态构造函数进行进一步的初始化工作。 5. **唯一性**:每个类只能有一个静态构造...
}}// 输出结果:// 父类静态代码块:fatherStr(静态字段初始化值)// 子类静态代码块:childStr(静态字段初始化值)通过上述输出结果,我们可以明确父类的静态代码块先于子类的静态字段初始化执行。这是因为静态...