1.初始化顺序
先看一个例子
class Counter {
int i;
Counter() {
//i = 7;
}
}
public class Test {
public static void main(String[] args) {
System.out.println(new Counter().i);
}
}我们执行这段程序,当i=7被注释掉,打印出0。这说明,成员变量(也就是Field)是有默认值的,int类型的默认值就是0。
当没有被注释掉,打印出的是7而不是0,可以看出自动初始化发生在构造器被调用之前。说明,想要构造器在堆中生成了一个对象,首先得保证这个对象中所有的成员变量都初始化过了,然后才能成功创建对象。
再看一个稍微复杂点的例子
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f();
}
}
class Window {
Window(int marker) {
System.out.println("window(" + marker + ")");
}
}
class House {
Window w1 = new Window(1);
House() {
System.out.println("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f() {
System.out.println("f()");
}
Window w3 = new Window(3);
}
/*
* output:
window(1)
window(2)
window(3)
House()
window(33)
f()
*/通过结果可以得到结论:
1.变量定义的先后顺序决定了初始化顺序。
2.即使变量定义散布于构造方法定义之间,也是在构造器执行前进行初始化。
2.静态成员变量的初始化
1.静态成员变量是不依赖于对象的。无论创建多少对象,静态成员变量置占据一份存储区域,只会初始化一次。
2.类不是立即被加载的,只有用到的时候(比如创建对象或访问静态变量或静态方法)才会加载然后初始化静态成员变量,注意当访问编译期已确定的static final成员变量不会类加载,因为这种变量是编译期已知的常量。
注:static final int a = Random.nextInt(47) 这种虽然是static final修饰的变量,但是a的值运行期才可知所以并不是编译期常量。
3.当访问静态成员变量(非编译期可知的)或静态方法时。如果类还未初始化,需要对类中静态成员变量进行初始化。非静态成员变量是依赖于对象的,所以并不会涉及非静态成员的初始化。
4.当通过构造方法创建对象时,如果类已经初始化了,那么静态成员变量便不再初始化,只需要对非静态成员变量进行初始化。如果类还未初始化,静态与非静态的成员变量都会进行初始化。初始化顺序为静态先于非静态。等所有成员变量都已经初始化后,再执行构造方法,在堆中生成对象。(其实是类加载与创建对象两个过程)
根据以上四点来分析TIJ中的一个示例(p95)
class Bowl {
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
void f1(int marker) {
System.out.println("f1(" + marker + ")");
}
}
class Table {
static Bowl bowl1 = new Bowl(1);
Table() {
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard() {
System.out.println("cupboard()");
bowl4.f1(2);
}
void f3(int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
public class StaticInialization {
public static void main(String[] args) {
System.out.println("Creating new cupboard in main()");
new Cupboard();
System.out.println("Creating new cupboard in main()");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
Bowl b = new Bowl(1); //自己额外添加的
}
先给出输出结果:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
cupboard()
f1(2)
Creating new cupboard in main()
Bowl(3)
cupboard()
f1(2)
Creating new cupboard in main()
Bowl(3)
cupboard()
f1(2)
f2(1)
f3(1)
分析:
1.程序首先进入main方法,这是静态方法。所以得对StaticInitialization这个类加载,table与cupboard这两静态成员会进行初始化,而非静态成员b不会初始化。
2.table初始化:static Table table = new table(); 根据上文提到的第四点,table类中静态成员bowl1与bowl2先初始化,然后执行构造方法。
所以先打印出
Bowl(1)
Bowl(2)
Table()
f1(1)
3.cupboard初始化:static Cupboard cupboard = new Cupboard(); 根据上文提到的第四点,cupboard类中静态成员bowl4与bowl5先初始化,随后非静态成员bowl3初始化,最后执行构造方法。即输出:
Bowl(4)
Bowl(5)
Bowl(3)
cupboard()
f1(2)
4.StaticInitialization类初始化完成后进入main方法,打印一句Creating new cupboard in main(),随后到new Cupboard()这一句想要生成Cupboard对象。因为Cupboard类里静态变量已经初始化过了,所以只需对非静态成员变量bowl3初始化,最后执行构造方法。
输出:
Creating new cupboard in main()
Bowl(3)
cupboard()
f1(2)
5.程序继续往下执行,打印一句Creating new cupboard in main(),随后到new
Cupboard()这一句。过程与上一段相同。也是输出:
Creating new cupboard in main()
Bowl(3)
cupboard()
f1(2)
6.执行:table.f2(1);cupboard.f3(1);
输出:
f2(1)
f3(1)
3.代码块
当程序中出现代码块时,尽管看起来像是方法,但是其初始化过程与成员变量初始化过程是一样的。
静态代码块,在首次生成对象,或者首次访问类的静态成员变量或静态方法时执行一次。此后都不会再执行。
非静态代码块,每次生成新的对象时都会执行一次。