Java类的初始化
初始化顺序:
1、当第一次通过构造函数创建一个对象,第一次访问类的static方法(最典型的如main函数,另外构造函数其实也是static的),或者是访问类的static成员时,JVM都要先在classpath中搜索类的.class文件,创建一个Class对象放在JVM进程的内存中,然后会执行类的静态初始化代码(static成员的初始化,static block)。并且静态初始化代码只会在类首次装载,即创建类的Class对象时执行。这一阶段称为类的静态初始化阶段。
2、当创建类对象时:new Dog(),如果类还未装载入JVM的内存,那么会首先执行上述的初始化,然后会给类的non-static成员(实例变量)分配内存,并且这部分内存会都置为0,即各种类型的实例变量会被赋值为该类型与“0”对应的值;数值类型就是0,布尔类型就是false(不过要注意Java中的数值类型不会自动转换成布尔类型)。这一阶段称为自动(默认)初始化阶段。
3、执行完自动初始化之后才轮到实例变量在声明时程序员指定的初始化(initialized at the point of definition),如:
class Person { private int age = 21; }
在new Person()时,实例变量age会首先被自动初始化为0,然后才会被赋值为21,也就是说即使我们在代码中声明实例变量时不赋予初始值,编译器也不会给出变量未初始化的错误。
《Think in Java》中的一个例子:
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 StaticInitialization { 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(1) Bowl(2) Table() f1(1) Bowl(4) Bowl(5) Bowl(3) Cupboard() f1(2) Creating new Cupboard() in main Bowl(3)(Cupboard的实例变量,每次创建一个实例都会执行初始化) Cupboard() f1(2) Creating new Cupboard() in main Bowl(3) Cupboard() f1(2) f2(1) f3(1)
初始化块:
静态初始化块
public class Spoon { static int i = 100; static int k; static { k = 47; } }
在静态初始化阶段执行,只能用于static成员的初始化,non-static成员无法在这个块中引用(与static method中无法引用non-static成员是一个道理)。其目的就是将static成员的初始化逻辑全部写在这个块中。上面例子中static int i = 100;与静态初始化块是平级的,谁先定义就谁先执行。
非静态初始化块
public class Person { private int i = 10; private int k; // non-static初始化块 { k = 88; } }
每次new一个对象的时候都会执行non-static初始化块,目的就是将实例变量的初始化逻辑都写在这个块中,因为有可能有些实例变量的初始化逻辑比较复杂,不能像上面例子中的“i”那样一句话就搞定。non-static初始化块只能初始化实例变量,不能引用到static的变量。
初始化块与实例变量声明时指定的初始化赋值语句的优先级是相同的,谁先定义就先执行谁。