对于实例变量而言,它属于Java 对象本身,每次程序创建Java 对象时都需要为实例变量分配内存空间,并执行初始化。从程序运行的角度来看,每次创建Java 对象都会为实例变量分配内存空间,并对实例变量执行初始化。从语法角度来看,程序可以在3个地方对实例变量执行初始化:
- 定义实例变量时指定初始值;
- 非静态初始化块中对实例变量指定初始值;
- 构造器中对实例变量指定初始值。
其中第1、2种方式(定义时指定的初始值和非静态初始化块中指定的初始值)比第3种方式(构造器中指定初始值)更早执行,但第1、2种方式的执行顺序与它们在源程序中的排列顺序相同。下面程序示范了实例变量的初始化时机。
class Cat { //定义 name、age两个实例变量 String name; int age; //使用构造器初始化 name、age两个实例变量 public Cat(String name , int age) { System.out.println("执行构造器"); this.name = name; this.age = age; } { System.out.println("执行非静态初始化块"); weight = 2.0; } //定义时指定初始值 double weight = 2.3; public String toString() { return "Cat[name=" + name + ",age=" + age + ",weigth=" + weight + "]"; } } public class InitTest { public static void main(String[] args) { Cat cat = new Cat("kitty" , 2); //① System.out.println(cat); Cat c2 = new Cat("Jerfield" , 3); //② System.out.println(c2); } }
上面程序演示了Java 对象的3种初始化方式:构造器、初始化块和定义变量时指定的初始值。每当程序调用指定构造器来创建Java对象时,该构造器必然会获得执行的机会。除此之外,该类所包含的非静态初始化块将会获得执行的机会,而且总是在构造器执行之前获得执行。
当程序执行①行代码创建第一个 Cat 对象的时候,程序将会先执行 Cat 类的非静态初始化块,再调用该Cat 类的构造器来初始化该Cat实例。执行完①行代码后该Cat对象的weight实例变量的值为2.3,而不是初始化块中指定的2.0。这是因为,初始化块中指定初始值,定义weight 时指定初始值,都属于对该实例变量执行的初始化操作,它们的执行顺序与它们在源程序中的排列顺序相同。在本程序中,初始化块中对weight 的赋值位于定义weight 语句之前,因此程序将先执行初始化块中的初始化操作,执行完成后weight 实例变量的值为 2.0;然后再执行定义weight 时指定的初始值,执行完成后weight 实例变量的值为 2.3。从这个意义上来看,初始化块中对weight 所指定的初始化值每次都将被2.3 所覆盖。当执行②行代码再次创建一个 Cat 对象时,程序将再一次调用非静态初始化块,相应的构造器来初始化Cat 对象。
很多人在看这个程序时可能会感到奇怪,会认为执行创建Cat 对象后weight 实例变量的值应该是2.0,而不是2.3。因为他们认为,double weight = 2.3;代码应该先获得执行。但实际上,定义变量时指定的初始值和初始化块中指定的初始值的执行顺序,与它们在源程序中的排列顺序相同。
经过上面分析不难发现:构造器、非静态初始化块、定义变量时指定的初始值,三者的作用完全类似,都用于对实例变量指定初始值。经过编译器处理之后,它们对应的赋值语句都被合并到构造器中。在合并过程中,定义变量语句转换得到的赋值语句、初始化块里的语句转换得到的赋值语句,总是位于构造器的所有语句之前;合并后,两种赋值语句的顺序保持它们在源代码中的顺序。
转载请注明:零五宝典 » Java中实例变量的初始化时机