1 public class MainTest
2 {
3 static void Main(string[] agrs)
4 {//断点① (没错,是花括号)
5 Console.WriteLine(Test1.Getddd());//断点② (调用静态方法)
6 Console.WriteLine(Test1.ddd);//断点③ (调用静态字段)
7 }
8 };
9 public class Test1
10 {
11 public static string ddd = Test2.kkk;//断点④
12 public static string Getddd()
13 {
14 return "ddd";//断点⑤
15 }
16 };
17 public class Test2
18 {
19 public static string kkk = "kkk";//断点⑥
20 };
2 {
3 static void Main(string[] agrs)
4 {//断点① (没错,是花括号)
5 Console.WriteLine(Test1.Getddd());//断点② (调用静态方法)
6 Console.WriteLine(Test1.ddd);//断点③ (调用静态字段)
7 }
8 };
9 public class Test1
10 {
11 public static string ddd = Test2.kkk;//断点④
12 public static string Getddd()
13 {
14 return "ddd";//断点⑤
15 }
16 };
17 public class Test2
18 {
19 public static string kkk = "kkk";//断点⑥
20 };
问题一:上面的代码中,我定义了六个断点,你不妨思考一下,六个断点处的代码执行顺序如何。下面有两个观点,你认同哪个呢?
观点1 : ① -- ⑤ -- ② -- ⑥ -- ④ -- ③
观点2 : ⑥ -- ④ -- ① -- ⑤ -- ② -- ③
(如果你还有其他观点,欢迎跟帖讨论)
如果你支持观点1,那你就很值得把这篇文章一看了。如果你肯定观点2是正确的,那说明你已经出师,无需再看下文讲解。
的确,观点2是正确。上面的代码主要的关键点有:
1.函数中调用静态字段。
2.函数中调用静态方法。
3.类的静态字段中调用另一个类的静态字段。
我们知道,类中的静态字段在整个程序中只会初始化一次,而静态方法却是每调用一次都要执行一次。
问题二:我们的仅有一次的类中的静态字段是何时被初始化的呢?
观点1 : 应用程序加载时就先把所有的静态字段都初始化一次。
观点2 : 当第一次调用到某个静态字段的语句时才去初始化那个静态字段,并且把这个类中的其他静态字段也初始化。
观点3 : 当第一次调用到某个静态字段的语句时才去初始化那个静态字段,并且只初始化这一个静态字段。
又要做选择了,你选择哪个呢?答案是~~~~~~~~~~~都不对-_-!
正确的初始化时机,我的理解是:当某个类第一次被实例化或类的静态方法第一次被调用或类的静态字段第一次准备要调用前,就尝试去初始化这个类的所有静态字段。为了验证这一点,我可以把上面的代码简单的修改一下:
1 public class MainTest
2 {
3 static void Main(string[] agrs)
4 {//断点① (没错,是花括号)
5 Console.WriteLine(Test1.Getddd());//断点② (测试调用静态方法时类Test1中执行的顺序如何)
6 //Test1 t1 = new Test1();//断点③ (测试初始化类时类Test1中执行的顺序如何)
7 }
8 };
9 public class Test1
10 {
11 public static string ddd = "kkk";//断点④
12 public static string Getddd()
13 {
14 return "ddd";//断点⑤
15 }
16 };
2 {
3 static void Main(string[] agrs)
4 {//断点① (没错,是花括号)
5 Console.WriteLine(Test1.Getddd());//断点② (测试调用静态方法时类Test1中执行的顺序如何)
6 //Test1 t1 = new Test1();//断点③ (测试初始化类时类Test1中执行的顺序如何)
7 }
8 };
9 public class Test1
10 {
11 public static string ddd = "kkk";//断点④
12 public static string Getddd()
13 {
14 return "ddd";//断点⑤
15 }
16 };
我们可以看到,代码执行的顺序是:1 -- 4 -- 5 -- 2 。第一次调用了Test1.Getddd()方法时,就对静态字段ddd进行了初始化。我们可以修改代码,在Main函数里先实例化一个Test1,这时我们可以看到当实例化一个类的时候静态字段也被初始化了。
问题三:为什么说上文说"类中的静态字段第一次准备要调用之前"?
"准备要调用之前"指的是一个类中(如第一段代码中的Class Test1中执行初始化ddd时要调用到的Class Test2中的静态字段kkk)或是在一个函数体中(如第一段代码中Main函数体中将要调用的Class Test1中的静态字段ddd)中将要调用到某个类的静态字段时,编译器会首先检查到这一情况,最先将该静态字段所属的类中的所有的静态字段初始化,再开始执行类或函数中要执行的代码。这就说明了为什么第一段代码中没有先执行断点①而是先去初始化了那些静态字段。
因此,总结如上,头脑中应该对于一个类、一个静态字段、一个静态方法、一个静态属性(执行顺序和静态方法几乎一样)、类中的构造函数等等的执行先后顺序有了比较清晰的了解。因此我个人总结了如下(希望批评指正):
1.当一个类被实例化时,首先检查类中的静态字段是否已经初始化,如果没有初始化,将最先执行静态字段的初始化代码。(如果静态字段的初始化代码中又调用了另一个类中的静态字段,将最优先执行另一个类中的静态字段的初始化。)接着,将执行非静态字段的代码,然后再执行类的构造函数。
2.当调用类的静态方法或属性时,也是首先检查类中的静态字段是否已经初始化,如果没有初始化,将最先执行静态字段的初始化代码。接着再执行静态方法或属性内部的代码。
3.某个函数体中存在使用了某个类的静态字段时,无论静态字段在函数体的什么位置,只要这个静态字段没有被初始化,将优先于执行这个函数之前,执行该静态字段的初始化(实际上将该类中的所有静态字段都初始化了)。
以上包含很多个人观点,有不对的地方希望大家批评指正!