初始化顺序(详细过程可看继承与初始化部分,相当于没有基类的子类)
1.静态成员变量或子句只有在首次new该类对象或者首次访问该类成员变量或方法时被执行一次;
2.非静态成员变量或子句在每次new该类对象时都会初始化;
3.构造器在成员变量和子句初始化之后才执行,而且需要new语句才会被执行;
4.成员变量和子句之间顺序执行;
5.包含main()方法的类,也需要先初始化静态成员变量,再执行main()方法。
所有的static对象和static代码段都会在加载时依程序中的顺序(即,定义类时的书写顺序)而依次初始化。当然,定义为static的东西只会在被初始化一次。
当通过new 类名()产生一个新对象时,顺序为:
1所有.静态成员变量或子句(仅一次)
2.所有非静态成员变量或子句(new几个对象,执行几次)
3.构造器当通过类名直接访问某类的静态成员变量或者方法时,顺序为:
1.所有静态成员变量或子句(仅一次)
2.访问变量或方法
对象创建过程
假设有一个Dog类
- 构造器实际上是静态方法,因此,首次创建类型为Dog的对象时,或者Dog类的静态方法、静态域首次被访问时,Java解释器必须查找类的路径,以定位Dog.class文件;
- 然后载入Dog.class文件,有关静态初始化的所有动作被执行。因此,静态初始化只在Class对象首次加载的时候进行一次;
- 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间;
- 这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对于数字来说是0,对于布尔类型和字符类型也相同),而引用则被设置成了null;
- 执行所有出现在成员变量定义处的初始化动作;
- 执行构造器。
继承与初始化
package thinkinginjava;
class Insect {
private int i = 9;
protected int j;
Insect() {
System.out.println("i = " + i + ", j = " + j);
j = 39;
}
private static int x1 = printInit("static Insect.x1 initialized");
static int printInit(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect {
private int k = printInit("Beetle.k initialized");
public Beetle() {
System.out.println("k = " + k);
System.out.println("j = " + j);
}
private static int x2 = printInit("static Beetle.x2 initialized");
public static void main(String[] args) {
System.out.println("Beetle constructor");
Beetle b = new Beetle();
}
}
结果:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
- 加载类(子类→基类→基类的基类……)
在Beetle上运行Java时,所发生的第一件事就是试图访问Beetle.main()(一个static方法),因为类在其static成员首次被访问时加载,于是加载器开始启动并找出Beetle类的编译代码(在名为Beetle.class的文件中)。在对它进行加载的过程中,编译器注意它有一个基类(这是由关键字extends得知的),于是它继续加载。不管你是否打算产生一个该基类的对象,这都要发生。
如果该基类还有其自身的基类,那么第二个基类将会被加载,以此类推。 - static初始化(根基类→根基类的子类→根基类子类的子类……)
根基类的static初始化先进行,然后是它的子类,以此类推,这种顺序很重要,因为导出类的static初始化可能会依赖于基类成员能否被正确的初始化。 - 设置默认值
类加载完毕后,对象就可以被创建了。对象中所有的基本类型被设置成0,引用被设置为null——这是通过将对象内存设置为二进制零值而一举生成的(在加载类时,我们已经能够得知该对象占有多少内存了,这其中包括基类的非静态成员变量,子类的非静态成员变量,它们都在这块内存中,会被同时设置为默认值)。 - 调用子类构造器时先调用基类构造器
显示或者隐式调用基类的构造器 - 基类构造器完成之后,实例变量按其次序被初始化
- 构造器的其余部分被执行
(当基类还有基类时,会以相同的顺序执行上述三个过程,直至根基类)