java变量,初始化快,构造函数的执行顺序

转自 http://blog.csdn.net/macheng365/article/details/6403050

——————————————————————————————————————————————————

对于JAVA中类的初始化是一个很基础的问题,其中的一些问题也是易被学习者所忽略。当在编写代码的时候碰到时,常被这些问题引发的错误,感觉莫名其妙。而且现在许多大公司的面试题,对于这方面的考查也是屡试不爽。不管基于什么原因,我认为,对于java类中的初始化问题,有必要深入的了解。Java类的初始化,其实就是它在JVM的初始化问题(类加载的问题),对于它在JVM中的初始化是一个相当复杂的问题,是给专家们来探讨的,所以在这里我只是对一些容易忽略的问题,发表一下个人观点:
1,在一个类的内部(不考虑它是另一个类的派生类):很多人认为,类的成员变量是在构造方法调用之后再初始化的,先不考虑这种观点的正确性,先看一下下面的代码:

[java] view
plain
copyprint?

  1. class Test01...{
  2. public Test01(int i)...{
  3. System.out.println("Test01 of constractor : " + i);
  4. }
  5. }
  6. public class Test02 ...{
  7. private Test01 t1 = new Test01(1);
  8. private int n = 10;
  9. public Test02()...{
  10. System.out.println("Test02 of constructor : " + n);
  11. }
  12. private Test01 t2 = new Test01(2);
  13. public static void main(String[] args) ...{
  14. Test02 test = new Test02();
  15. }
  16. }
  17. 输出的结果为:
  18. Test01 of constractor : 1
  19. Test01 of constractor : 2
  20. Test02 of constructor : 10

class Test01...{
public Test01(int i)...{
System.out.println("Test01 of constractor : " + i);
}
}
public class Test02 ...{
private Test01 t1 = new Test01(1);
private int n = 10;

public Test02()...{
System.out.println("Test02 of constructor : " + n);
}
private Test01 t2 = new Test01(2);
public static void main(String[] args) ...{
Test02 test = new Test02();
}

}
输出的结果为:
Test01 of constractor : 1
Test01 of constractor : 2
Test02 of constructor : 10

通过输出,可见当生成Test02的实例test时,它并不是首先调用其构造方法而是先是成员变量的初始化,而且成员的初始化的顺序以成员变量的定义顺序有关,先定义的先初始化,初始化后再调用构造方法。其实成员变量的初始化,在类的所有方法调用之前进行,包括构造方法
当类中有Static
修饰的成员呢?测试下面一段代码:

[java] view
plain
copyprint?

  1. public class Test03 ...{
  2. private int i1 = printCommon();
  3. private static int i2 = printStatic();
  4. public Test03()...{
  5. }
  6. public static int printCommon()...{
  7. System.out.println("i1 is init!");
  8. return 1;
  9. }
  10. public static int printStatic()...{
  11. System.out.println("i2 is init!");
  12. return 2;
  13. }
  14. public static void main(String[] args) ...{
  15. Test03 t = new Test03();
  16. }
  17. }
  18. 输出结果为:
  19. i2 is init!
  20. i1 is init!

public class Test03 ...{
private int i1 = printCommon();
private static int i2 = printStatic();

public Test03()...{

}
public static int printCommon()...{
System.out.println("i1 is init!");
return 1;
}
public static int printStatic()...{
System.out.println("i2 is init!");
return 2;
}
public static void main(String[] args) ...{
Test03 t = new Test03();
}
}

输出结果为:
i2 is init!
i1 is init!

可见static的成员比普通的成员变量先初始化。
我们都知道,如果一个类的成员变量没有在定义时,系统会给予系统默认的值,有=号的就直接给予右值,系统在给予初值和=号给予值这2中方式,在执行时间上有先后吗?为了测试,我编写了如下代码:

[java] view
plain
copyprint?

  1. public class Test04 ...{
  2. private static Test04 t1 = new Test04();
  3. private static int i1;
  4. private static int i2 = 2;
  5. public Test04()...{
  6. i1++;
  7. i2++;
  8. }
  9. public static void main(String[] args) ...{
  10. Test04 t2 = new Test04();
  11. System.out.println("t2.i1 = " + t2.i1);
  12. System.out.println("t2.i2 = " + t2.i2);
  13. }
  14. }
  15. 我们先预计一下输出,可能有几种答案:2和3,3和3,2和2
  16. 执行代码后:
  17. t2.i1 = 2
  18. t2.i2 = 3

public class Test04 ...{
private static Test04 t1 = new Test04();
private static int i1;
private static int i2 = 2;

public Test04()...{
i1++;
i2++;
}

public static void main(String[] args) ...{
Test04 t2 = new Test04();
System.out.println("t2.i1 = " + t2.i1);
System.out.println("t2.i2 = " + t2.i2);
}
}
我们先预计一下输出,可能有几种答案:2和3,3和3,2和2
执行代码后:
t2.i1 = 2
t2.i2 = 3

为什么是2和3呢?其实代码的执行顺序是这样的:首先执行给t1,i1,i2分别给予初始值null,0,0,再执行
Test04
t1 =new Test04(),这样i1++,i2++被执行,i1,i2都变为1,执行完毕后接着执行int i1; i1,i2的值仍然是1,1,当执行int
i2 = 2时i2被赋予了值,即i1 = 1,i2=2;再执行Test04 t2 = new Test04(),i1,i2再执行++,此时i1 =2,i2
=3,输出i1,i2,结果就是:t2.i1 = 2,t2.i2 = 3。
通过上面的代码我们可以认为系统默认值的给予比通过等号的赋予先执行。
2,一个类还有上层的类,即父类:
     
当生成一个子类时,大家到知道会调用父类的构造方法。如果子类和父类中都有Static的成员变量呢,其实我们在深入分析一个类的内部初始化后,对于存在父类的类的初始化其实原理都一样,具体以下面的代码为例:

[java] view
plain
copyprint?

  1. class SuperClass ...{
  2. static...{
  3. System.out.println("SuperClass of static block");
  4. }
  5. public SuperClass()...{
  6. System.out.println("SuperClass of constracutor");
  7. }
  8. }
  9. public class SubClass extends SuperClass...{
  10. static...{
  11. System.out.println("SubClass of static block");
  12. }
  13. public SubClass()...{
  14. System.out.println("SubClass of constracutor");
  15. }
  16. public static void main(String[] args)...{
  17. SuperClass t = new SubClass();
  18. }
  19. }
  20. 输出结果:
  21. SuperClass of static block
  22. SubClass of static block
  23. SuperClass of constracutor
  24. SubClass of constracutor

class SuperClass ...{
static...{
System.out.println("SuperClass of static block");
}

public SuperClass()...{
System.out.println("SuperClass of constracutor");
}
}

public class SubClass extends SuperClass...{
static...{
System.out.println("SubClass of static block");
}

public SubClass()...{
System.out.println("SubClass of constracutor");
}

public static void main(String[] args)...{
SuperClass t = new SubClass();
}
}
输出结果:
SuperClass of static block
SubClass of static block
SuperClass of constracutor
SubClass of constracutor

可见当父类,和子类有Static时,先初始化Static,再初始化子类的Static,再初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法。
父类上层还有父类时,总是先执行最顶层父类的Static-->派生类Static-->派生类Static-->.......-->子类Static-->顶层父类的其他成员变量-->父类构造方法-->
派生类的其他成员变量 --> 派生类构造方法-->
...............-->子类其他成员变量-->子类构造方法
讨论到继承,就不得提一下多态:
如果父类构造方法的代码中有子类中被重写得方法,当执行这样的语句
SuperClass
super = new SubClass();
初始化时调用父类的构造方法,是执行父类的原方法,还是执行子类中被重写的方法呢?

[java] view
plain
copyprint?

  1. class SuperClass...{
  2. public SuperClass()...{
  3. System.out.println("SuperClass of constructor");
  4. m();
  5. }
  6. public void m()...{
  7. System.out.println("SuperClass.m()");
  8. }
  9. }
  10. public class SubClassTest extends SuperClass ...{
  11. private int i = 10;
  12. public SubClassTest()...{
  13. System.out.println("SubClass of constructor");
  14. super.m();
  15. m();
  16. }
  17. public void m()...{
  18. System.out.println("SubClass.m(): i = " + i);
  19. }
  20. public static void main(String[] args)...{
  21. SuperClass t = new SubClassTest();
  22. }
  23. }
  24. 可能很多人会认为输出为:
  25. SuperClass of constructor
  26. SubClass.m(): i = 10
  27. SubClass of constructor
  28. SuperClass.m()
  29. SubClass.m(): i = 10
  30. 其实不然!
  31. 正确输出为:
  32. SuperClass of constructor
  33. SubClass.m(): i = 0
  34. SubClass of constructor
  35. SuperClass.m()
  36. SubClass.m(): i = 10
  37. 在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte  int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。
  38. 下面是我设计的一道完整的初始化例子,可测试你对类的初始化问题是否完整掌握:
  39. 写出程序运行的结果:
  40. class A...{
  41. private int i = 9;
  42. protected static int j;
  43. static...{
  44. System.out.println("-- Load First SuperClass of static block start!-- ");
  45. System.out.println("j = " + j);
  46. System.out.println("-- Load First SuperClass of static block End  -- ");
  47. }
  48. public A()...{
  49. System.out.println("------- Load SuperClass of structor start --------");
  50. System.out.println("Frist print j = " + j);
  51. j = 10;
  52. m();
  53. System.out.println("k = " + k);
  54. System.out.println("Second print j = " + j);
  55. System.out.println("-----------  Load  SuperClass End    ----------- ");
  56. }
  57. private static int k = getInt();
  58. public static int getInt()...{
  59. System.out.println("Load SuperClass.getInt() ");
  60. return 11;
  61. }
  62. static...{
  63. System.out.println("--- Load Second SuperClass of static block!-------");
  64. System.out.println("j = " + j);
  65. System.out.println("k = " + k);
  66. System.out.println("-- Load Second SuperClass of static block End -- ");
  67. }
  68. public void m()...{
  69. System.out.println("SuperClass.m() , " + "j = " +j);
  70. }
  71. }
  72. class B extends A ...{
  73. private  int a = 10;
  74. static...{
  75. System.out.println("---- Load SubClass of static block!------");
  76. System.out.println("-- Load SubClass of static block End -- ");
  77. }
  78. public B()...{
  79. System.out.println("Load SubClass of structor");
  80. m();
  81. System.out.println("---   Load SubClass End  ---- ");
  82. }
  83. public void m()...{
  84. System.out.println("SubClass.m() ," + "a = " + a );
  85. }
  86. }
  87. public class Test1...{
  88. public static void main(String[] args)...{
  89. A a = new B();
  90. }
  91. }
  92. 正确的答案为:
  93. -- Load First SuperClass of static block start!--
  94. j = 0
  95. -- Load First SuperClass of static block End  --
  96. Load SuperClass.getInt()
  97. --- Load Second SuperClass of static block!-------
  98. j = 0
  99. k = 11
  100. -- Load Second SuperClass of static block End --
  101. ---- Load SubClass of static block!------
  102. -- Load SubClass of static block End --
  103. ------- Load SuperClass of structor start --------
  104. Frist print j = 0
  105. SubClass.m() ,a = 0
  106. k = 11
  107. Second print j = 10
  108. -----------  Load  SuperClass End    -----------
  109. Load SubClass of structor
  110. SubClass.m() ,a = 10
  111. ---   Load SubClass End  ----

class SuperClass...{
public SuperClass()...{
System.out.println("SuperClass of constructor");
m();
}
public void m()...{
System.out.println("SuperClass.m()");
}
}
public class SubClassTest extends SuperClass ...{
private int i = 10;
public SubClassTest()...{
System.out.println("SubClass of constructor");
super.m();
m();
}
public void m()...{
System.out.println("SubClass.m(): i = " + i);
}
public static void main(String[] args)...{
SuperClass t = new SubClassTest();
}
}
可能很多人会认为输出为:
SuperClass of constructor
SubClass.m(): i = 10
SubClass of constructor
SuperClass.m()
SubClass.m(): i = 10
其实不然!
正确输出为:
SuperClass of constructor
SubClass.m(): i = 0
SubClass of constructor
SuperClass.m()
SubClass.m(): i = 10
在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。
下面是我设计的一道完整的初始化例子,可测试你对类的初始化问题是否完整掌握:
写出程序运行的结果:
class A...{
private int i = 9;
protected static int j;
static...{
System.out.println("-- Load First SuperClass of static block start!-- ");
System.out.println("j = " + j);
System.out.println("-- Load First SuperClass of static block End -- ");
}

public A()...{
System.out.println("------- Load SuperClass of structor start --------");
System.out.println("Frist print j = " + j);
j = 10;
m();
System.out.println("k = " + k);
System.out.println("Second print j = " + j);
System.out.println("----------- Load SuperClass End ----------- ");
}

private static int k = getInt();

public static int getInt()...{
System.out.println("Load SuperClass.getInt() ");
return 11;
}
static...{
System.out.println("--- Load Second SuperClass of static block!-------");
System.out.println("j = " + j);
System.out.println("k = " + k);
System.out.println("-- Load Second SuperClass of static block End -- ");
}

public void m()...{
System.out.println("SuperClass.m() , " + "j = " +j);

}
}

class B extends A ...{
private int a = 10;

static...{
System.out.println("---- Load SubClass of static block!------");
System.out.println("-- Load SubClass of static block End -- ");
}

public B()...{
System.out.println("Load SubClass of structor");
m();
System.out.println("--- Load SubClass End ---- ");
}

public void m()...{
System.out.println("SubClass.m() ," + "a = " + a );
}
}

public class Test1...{
public static void main(String[] args)...{
A a = new B();
}
}
正确的答案为:
-- Load First SuperClass of static block start!--
j = 0
-- Load First SuperClass of static block End --
Load SuperClass.getInt()
--- Load Second SuperClass of static block!-------
j = 0
k = 11
-- Load Second SuperClass of static block End --
---- Load SubClass of static block!------
-- Load SubClass of static block End --
------- Load SuperClass of structor start --------
Frist print j = 0
SubClass.m() ,a = 0
k = 11
Second print j = 10
----------- Load SuperClass End -----------
Load SubClass of structor
SubClass.m() ,a = 10
--- Load SubClass End ----

下面需要说明的一点也是至关重要的一点:那就是成员变量的初始化和非static初始化块之间的执行顺序是按照他们出现的先后顺序来执行的

[java] view plaincopyprint?

  1. public class Test04
  2. {
  3. //下面的这两行代码放置的顺序,跟执行结果是有关系的
  4. private  String t1 = test();
  5. {
  6. System.out.println("初始化快!");
  7. }
  8. //上面的这两行代码放置的顺序,跟执行结果是有关系的
  9. private String test(){
  10. System.out.println("实例变量的执行过程");
  11. return "test";
  12. }
  13. public Test04()
  14. {
  15. System.out.println("构造方法!");
  16. }
  17. public static void main(String[] args)
  18. {
  19. Test04 t2 = new Test04();
  20. }
  21. }
时间: 2024-10-25 15:42:57

java变量,初始化快,构造函数的执行顺序的相关文章

Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究

随笔- 40  文章- 0  评论- 1 Java的初始化块.静态初始化块.构造函数的执行顺序及用途探究 Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念.下面探究一下它的执行顺序与可能的用途. 执行顺序 首先定义A, B, C三个类用作测试,其中B继承了A,C又继承了B,并分别给它们加上静态初始化块.非静态初始化块和构造函数,里面都是一句简单的输出. 主类Main里面也如法炮制. 1 class A { 2 st

Java变量初始化的时机

对于JAVA中变量的初始化是一个很基础的问题,其中的一些问题也是易被学习者所忽略.当在编写代码的时候碰到时,常被这些问题引发的错误,感觉莫名其妙.而且现在许多大公司的面试题,对于这方面的考查也是屡试不爽.以下是对java变量初始化的时机的分析. [java变量执行初始化的步骤] java是一门强类型语言,因此java语言规定每个变量必须先声明,然后才能使用,声明变量时必须指定该变量的数据类型.首先看下面这条语句的执行过程: int a = 5; 实际上面这条语句会被拆分成两个过程执行: (1)i

关于子类对象的构造函数和父类构造函数的执行顺序

我们分别为父类和子类添加显式的构造函数,代码如下: class Person     {         private int nAge;         protected string strName;         double douHeight;         public string strEateType;         //父类的构造函数         public Person()         {             Console.WriteLine("我

C#构造函数的执行顺序

<1> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 构造函数的执行顺序 { public class MyBaseClass { public MyBaseClass() { Console.Write("调用父类无参数的构造函数"); } public MyBaseClass(int i) { Console.Write(&q

继承构造函数的执行顺序

转自:http://blog.csdn.net/daheiantian/article/details/6438782 Code: #include <iostream> using namespace std; class A { public: A() { cout<<"int A::A()"<<endl; } A(A &a) { cout<<"int A::A(A &a)"<<endl

java中finally和return的执行顺序

注意:return的位置... 从这几个例子中可以看到,如果try之前没有有条件的return,则try..catch..finally语句块中的语句都是顺序执行(如果try中或者catch中 有return语句,那么先执行该return,然后执行finally, 如果finally中也有return, 该出的返回值会覆盖掉try 和 catch中的return值: 如果try..catch..finally语句块之后有return语句, try中.catch中.finally中都没有 (有条件

【细说Java】Java变量初始化顺序

Java的变量初始化顺序,对这里一直似懂非懂,面试的时候也经常被问到,但答的一直不好,现在整理记录一下,以后忘记了可以来看看. 程序分为两个部分,第一个部分不考虑继承,第二个部分考虑继承: (1)不考虑继承的情况 代码如下: public class JavaTest { public JavaTest() { System.out.println("执行JavaTest构造方法1"); } public JavaTest(String param) { System.out.prin

java 静态块和构造块等执行顺序分析学习

构造块:在类成员变量区域用大括号括起来的代码,和方法相比没有修饰,没有返回,没有参数:静态块:在构造块前加 static修饰静态代码块:静态块+静态变量非静态代码块:普通类成员变量+构造块 程序执行顺序:静态代码块->非静态代码块->类构造方法 1 public class Test { 2 3 static { 4 System.out.println("静态块"); 5 } 6 7 { 8 System.out.println("构造块"); 9 }

Java变量初始化问题

 class AMMM { public final static  int CHANG = 10;//注意并不是修饰符为final+static的域就是常量, //比如:final static int A = Random.nextInt();它并非编译期常量 public AMMM()//(1) { i = 100000; } {//(2) i = 1; mu = 2; } public static int i = 100;// (3) public int mu = 0;//(4)