静态成员的初始化顺序(C#,java)

静态成员的初始化顺序

时间:2012 年 10 月 20 日  分类:学习笔记 C# Java

前几天去参加了场笔试,里面考了静态构造函数,当时没做出来,现在对静态成员的初始化做一个总结。
在c#类中的静态成员有静态变量、静态函数和静态构造函数,而在java中是没有静态构造函数的,取而代之的是静态程序块。静态成员一般存放在静态区,而且是属于类的,所以我们可以不用实例化对象,直接调用静态函数,比如工具类的方法一般都声明为静态函数。c#和java对静态成员的初始化顺序是不一样的,下面我将分别对他们进行总结。

1.c#中静态成员的初始化顺序

为了更好的说明,我写了一个测试程序,为了让变量在初始化时有打印信息,我用成员函数对他们赋值,程序如下:

  
  
  1. class A
  2. {
  3. static int a = setA();//静态变量
  4. int a1 = setA1();//非静态变量
  5. private static int setA1()
  6. {
  7. Console.WriteLine("父类非静态变量");
  8. return 1;
  9. }
  10. public static int setA()
  11. {
  12. Console.WriteLine("父类静态变量");
  13. return 1;
  14. }
  15. public A()//构造函数
  16. {
  17. Console.WriteLine("父类构造函数");
  18. }
  19. static A()//静态构造函数
  20. {
  21. Console.WriteLine("父类静态构造函数");
  22. }
  23. }
  24. class B : A
  25. {
  26. static int b = setB();//静态变量
  27. int b1 = setB1();//非静态变量
  28. private static int setB1()
  29. {
  30. Console.WriteLine("子类非静态变量");
  31. return 1;
  32. }
  33. public static int setB()
  34. {
  35. Console.WriteLine("子类静态变量");
  36. return 1;
  37. }
  38. public B()//构造函数
  39. {
  40. Console.WriteLine("子类构造函数");
  41. }
  42. static B()//静态构造函数
  43. {
  44. Console.WriteLine("子类静态构造函数");
  45. }
  46. }
  47. class Program
  48. {
  49. static void Main(string[] args)
  50. {
  51. Console.WriteLine("第一次调用。。。");
  52. B b = new B();
  53. Console.WriteLine("第二次调用。。。");
  54. b = new B();
  55. }
  56. }

在上面我定义了一个父类A和一个子类B,再让子类实例化两次,并打印出初始化信息,结果如下:

  
  
  1. 第一次调用。。。
  2. 子类静态变量
  3. 子类静态构造函数
  4. 子类非静态变量
  5. 父类静态变量
  6. 父类静态构造函数
  7. 父类非静态变量
  8. 父类构造函数
  9. 子类构造函数
  10. 第二次调用。。。
  11. 子类非静态变量
  12. 父类非静态变量
  13. 父类构造函数
  14. 子类构造函数

从这里我们可以看到,静态变量和静态构造函数只会在类的第一次实例化时进行初始化,第二次就是正常的初始化了。在正常实例化中,初始化的顺序是:成员变量 -> 父类实例化 -> 构造函数。如果有静态类型的话,就会先初始化静态类型,于是顺序就变成了:静态变量 -> 静态构造函数 -> 成员变量 -> 父类实例化 -> 构造函数。在父类实例化中,顺序也是这样的。

1.1.一道笔试题

  
  
  1. class A
  2. {
  3. public static int X;
  4. static A()
  5. {
  6. X = B.Y + 1;
  7. }
  8. }
  9. class B
  10. {
  11. public static int Y = A.X + 1;
  12. static B() { }
  13. static void Main()
  14. {
  15. Console.WriteLine("X={0},Y={1}", A.X, B.Y);
  16. }
  17. }

程序会从B类中的Main()开始执行,所以先初始化静态变量Y,而Y要调用A.X,调用A类静态构造函数A(),此时B.Y未初始化默认为0,所以X=1,再回到B的静态变量初始化中Y就是2了。初始化完成后,进入Main(),打印X=1,Y=2。意外吧!

2.java中静态成员的初始化顺序

同样的,只不过静态构造函数用静态程序块代替。

  
  
  1. public class Main {
  2. public static void main(String[] args) {
  3. System.out.println("第一次调用。。。");
  4. B b = new B();
  5. System.out.println("第二次调用。。。");
  6. b = new B();
  7. }
  8. }
  9. class A {
  10. private static int a = setA();
  11. private int a1 = setA1();
  12. public static int setA() {
  13. System.out.println("父类静态变量");
  14. return 1;
  15. }
  16. private int setA1() {
  17. System.out.println("父类非静态变量");
  18. return 0;
  19. }
  20. public A() {
  21. System.out.println("父类构造函数");
  22. }
  23. static {
  24. System.out.println("父类静态程序块");
  25. }
  26. }
  27. class B extends A {
  28. private static int b = setB();
  29. private int b1 = setB1();
  30. private static int setB1() {
  31. System.out.println("子类非静态变量");
  32. return 1;
  33. }
  34. public static int setB() {
  35. System.out.println("子类静态变量");
  36. return 1;
  37. }
  38. public B() {
  39. System.out.println("子类构造函数");
  40. }
  41. static {
  42. System.out.println("子类静态程序块");
  43. }
  44. }

运行结果如下:

  
  
  1. 第一次调用。。。
  2. 父类静态变量
  3. 父类静态程序块
  4. 子类静态变量
  5. 子类静态程序块
  6. 父类非静态变量
  7. 父类构造函数
  8. 子类非静态变量
  9. 子类构造函数
  10. 第二次调用。。。
  11. 父类非静态变量
  12. 父类构造函数
  13. 子类非静态变量
  14. 子类构造函数

和c#的一样,只在第一次实例化时调用,但是初始化顺序却不一样。在java中正常实例化顺序是:父类实例化 -> 成员变量 -> 构造函数。加入静态类型后会先出示话所有的静态类型(包括父类和子类的),然后才是正常的初始化:父类类静态类型 -> 子类静态类型 -> 父类正常实例化 -> 成员变量 -> 构造函数。静态类型的初始化顺序都是先变量后程序块。

3.总结

c#的初始化顺序看起来比较有规律,父类在子类中初始化,先静态后常规;而java则是先初始化全部静态类型(先父后子),再父类实例化,最后子类。

文章地址: http://wuyuans.com/2012/10/static-initialization/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值