1. 加载类(所有的成员变量,无论是否是静态的,都赋了一个默认值)
2. 初始化静态成员变量
3. 初始化非静态类成员变量
4. 调用该类的构造器
这是每个类的初始化过程。整个程序的初始化过程,是一个类一个类的完成的;其中,可能加载某个类,但是并没有产生它的实例,也就是说,执行了前两步,但是没有执行3,4步。
示例程序:
class ITest1
{
static
{
System.out.println("Loading ITest1 ... /n");
}
public ITest1()
{
System.out.println("ITest1's constructor");
}
}
class ITest2
{
static int i=1;
static int j;
static
{
System.out.println("Loading ITest2 ... ");
System.out.println("In ITest2: i="+i+", j="+j+"/n");
}
public ITest2()
{
System.out.println("ITest2's constructor");
}
}
class ITest3
{
int i;int j=1;
boolean b;String s;
static
{
System.out.println("Loading ITest3 ... ");
}
public ITest3()
{
System.out.println("In ITest3: i="+i+", j="+j); /*如果定义时没有赋初值的话,JVM将赋一个默认值*/
System.out.println("In ITest3: b="+b);
System.out.println("In ITest3: s="+s);
}
// public void f(){int i;System.out.println("####"+i);}
}
public class InitTest extends ITest1
{
static int j=ITest2.i;
static
{
System.out.println("Loading InitTest ... ");
System.out.println("In InitTest: j="+j+"/n");
}
public static void main(String[] args)
{
System.out.println("Running main() ... ");
ITest1 it1=new ITest1();
ITest2 it2=new ITest2();
System.out.println("After creating it1 and it2, no loading coz only load once ... /n");
System.out.println("Creating it3 using new ... ...");
ITest3 it3=new ITest3();
}
}
输出结果:
E:/MyDoc/Exercises>java InitTest
Loading ITest1 ...
Loading ITest2 ...
In ITest2: i=1, j=0
Loading InitTest ...
In InitTest: j=1
Running main() ...
ITest1's constructor
ITest2's constructor
After creating it1 and it2, no loading coz only load once ...
Creating it3 using new ... ...
Loading ITest3 ...
In ITest3: i=0, j=1
In ITest3: b=false
In ITest3: s=null
================================================================================
本程序的执行顺序:
1. 加载类ITest1;
2. 初始化ITest1的静态成员变量,如果有的话;
3. 加载类ITest2;
4. 初始化ITest2的静态成员变量;
5. 加载InitTest;
6. 初始化InitTest的静态成员变量;
7. 找到入口函数main()开始逐句执行;
8. 创建了it1对象,但是由于类ITest1已经加载进来,所以不再重复加载。
9. 初始化ITest1的非静态成员变量,如果有的话;
10. 调用ITest1的构造器;
11. 创建了it2对象,但是由于类ITest2已经加载进来,所以不再重复加载。
12. 初始化ITest2的非静态成员变量,如果有的话;
13. 调用ITest2的构造器;
14. 创建it3对象。
it3对象创建过程如下:
1. 加载类ITest3;
2. 初始化其静态成员变量(如果有的话);
3. 初始化成员变量 i,j,b,s;
4. 调用ITest3的构造器;
=====================================说明========================================
说明1:初始化的时候总是先初始化父类,然后初始化子类;而清除(Clean up)的时候顺序正好相反。参见http://blog.csdn.net/fitzwilliam/archive/2006/03/21/630925.aspx
说明2:不管创建了多少个实例,类只加载一次。静态成员变量在类加载的时候初始化,同样只初始化一次。
说明3:java InitTest 需要把类InitTest加载进JVM。由于类InitTest继承自类Itest1,所以必须先把ITest1加载进来;另外,由于InitTest的静态成员初始化时调用了类ITest2的静态成员变量,所以也必须把ITest2加载进来。
说明4:类ITest3的加载时间说明JVM并不是在一开始就把程序中所有用到的类都加载进来,而是在某个类被用到的时候才把该类加载进来。“被用到的时候”有两种解释:一是使用new创建了该类的一个实例(如ITest3所示);二是调用了该类的静态变量或者静态方法(如ITest2所示)。
说明5:类成员变量(包括静态的)在初始化时,JVM会先赋给它一个默认值(int型为0;char为空字符;boolean为false;float,double为0.0;引用型为null)。如果在定义时赋了初值就把该初值赋给它;如果定义时未赋初值,在构造器中也未赋初值,就在调用的时候使用默认值。
说明6:局部成员变量则必须赋初值,否则如果在程序中调用的话,编译的时候会出错。(如注释掉的f()方法所示。)
=================================================================================
补充一点:
在类加载的时候,所有的成员变量,无论是否是静态的,都赋了一个默认值。注意,是在类加载的时候赋的,而不是在创建对象(new)的时候。
下面的例子可以证明:
public class Test3 extends Test4
{
int i=30;
Test3()
{
print();
i=40;
}
public static void main(String[] args)
{
// System.out.println(new Test3().i);
Test4 t4=new Test3();
}
void print()
{
System.out.println(i);
}
}
class Test4
{
int i=10;
Test4()
{
print();
i=20;
}
void print()
{
System.out.println(i);
}
}
OUTPUT:
E:/MyDoc/temp>java Test3
0
30
说明:由于运行时绑定(late-binding),所以创建Test4对象时,Test4的构造器所调用的方法print()是Test3重写后的方法,即Test3中的print()方法;此时,Test3对象还没有创建,所以Test3中的成员变量 i 还没有被初始化,其值为加载时赋的默认值 0.