类体内定义的变量被称为成员变量.如果定义该成员变量时没有使用static修饰,该成员变量又被成为非静态变量或实例变量;如果使用了static修饰,则该成员变量又可被成为静态变量或类变量.
对于实例变量而言,它属于Java对象本身,每次程序创建Java对象时都需要为实例变量分配内存空间,并执行初始化.
从程序运行的角度来看,每次创建Java对象都会为实例变量分配内存空间,并对实例变量执行初始化.
从语法角度来看,程序可以在3个地方对实例变量执行初始化:
1.定义实例变量时指定初始值;
2.非静态初始化块中对实例变量指定初始值;
3.构造器中对实例变量指定初始值.
其中第1,2种方式(定义时指定的初始值和非静态初始化块中指定的初始值)比起第3种方式(构造器中指定初始值)更早执行,但第1,2种方式的执行顺序与它们在源程序中的排列顺序相同.
package com.lic.array;
public class Demo09 {
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);
}
}
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+",weight="+weight+"]";
}
}
控制台输出:
执行非静态初始化块
执行构造器
Cat[name=kitty,age=2,weight=2.3]
执行非静态初始化块
执行构造器
Cat[name=Jerfield,age=3,weight=2.3]
以上代码代表Java对象的3种初始化方式:构造器,初始化块和定义变量时指定的初始值.每当程序调用指定构造器来创建Java对象时,该构造器必然会获得执行的机会.除此之外,该类所包含的非静态初始化块将会获得执行的机会,而且总是在构造器执行之前获得执行.
当程序执行
Cat cat = new Cat("kitty", 2);
创建第一个Cat对象的时候,程序将会先执行Cat类的非静态初始化块,再调用该Cat类的构造器来初始化该Cat实例.执行完该代码后,内存分分配如图;
从图中可以看出,该Cat对象的weight实例变量的值为2.3,而不是初始化块中指定的.这是因为,初始化块中指定初始值,定义weight时指定初始值,都属于对该实例变量执行的初始化操作,它们的执行顺序与在源程序中的排列顺序相同.在本程序中,初始化块中对weight的赋值位于定义weight语句之前,因此程序将先执行初始化块中的初始化操作,执行完成后weight实例变量的值为2.0;然后再执行定义weight时指定的初始值,执行完成后weight实例变量的值为2.3.从这个意义上来看,初始化块中对weight所指定的初始化值每次都将被2.3所覆盖.
当执行代码
Cat c2 = new Cat("Jerfield", 3);
再次创建一个Cat对象时,程序将再一次调用非静态初始化块,相应的构造器来初始化Cat对象.
定义实例变量时指定的初始值,初始化块中为实例变量指定初始值的语句的地位是平等的,当经过编译器处理后,它们都将被提取到构造器中.也就是说,对于类定义中的语句:
double weight = 2.3;
实际上会被分成如下2次执行.
1)double weight;:创建Java对象时系统根据该语句为该对象分配内存.
2)weight = 2.3;:这条语句将会被提取到Java类的构造器中执行.
示例如下.
package com.lic.array;
public class Demo10 {
// 定义count实例变量,并为之指定初始值
int count = 20;
{
// 初始化块中为count实例变量指定初始值
count = 12;
}
// 定义两个构造器
public Demo10(){
System.out.println(count);
}
public Demo10(String name){
System.out.println(name);
}
}
上面程序定义了一个count实例变量,既在定义该变量时指定了默认初始值,也在非静态初始化块中为该变量指定初始值.这个程序本身已经非常简单了,无需进行太多深入的讲解.
定义实例变量时指定的初始值.初始化块中为实例变量指定的初始值,构造器中为实例变量指定的初始值,三者的作用完全类似,都用于对实例变量指定初始值.经过编译器处理之后,它们对应的赋值语句都被合并到构造器中.在合并过程中,定义变量语句转换得到的赋值语句,初始化块里的语句转换得到的赋值语句,总是位于构造器的所有语句之前;合并后,两种赋值语句的顺序保持它们在源代码中的顺序.