字段初始化过程详解"/>
Java类中字段初始化过程详解
1. 初始化顺序
类中的变量定义顺序决定了初始化的顺序,即使分散到方法定义之间,变量定义仍然会在任何方法(包括构造器)调用之前就被初始化,看下面这段代码:
package com.yyj;public class OrderOfInitialization {public static void main(String[] args) {B b = new B();b.f();/** Initial class A with 1* Initial class A with 2* Initial class A with 3* Initial class B* Initial class A with 4* This is f()*/}
}class A {A(int value) {System.out.println("Initial class A with " + value);}
}class B {A a1 = new A(1); // 在构造器之前定义B() {System.out.println("Initial class B");a3 = new A(4); // 在构造器内定义}A a2 = new A(2); // 在构造器之后定义void f() {System.out.println("This is f()");}A a3 = new A(3); // 在尾部定义
}
在类 B
中,A
对象的定义分散到各个方法之间,但他们均在构造器执行前被初始化完成,其中有一个对象引用在构造器内被重新初始化。
a3
引用被初始化了两次:一次在构造器调用之前,另一次在构造器调用期间(第一个对象被丢弃了,因此稍后可能会被垃圾收集器回收)。
2. 静态数据的初始化
无论创建了多少对象,静态数据都只有一份存储空间。来看下面的代码:
package com.yyj;public class StaticInitialization {public static void main(String[] args) {System.out.println("Creating class B in main...");B b1 = new B();}static B b2 = new B();/** Initial class A with 2* Initial class A with 3* Initial class A with 1* Initial class B* This is A.f()* Creating class B in main...* Initial class A with 1* Initial class B* This is A.f()*/
}class A {A(int value) {System.out.println("Initial class A with " + value);}void f() {System.out.println("This is A.f()");}
}class B {A a1 = new A(1);static A a2 = new A(2);B() {System.out.println("Initial class B");a2.f();}static A a3 = new A(3);
}
静态字段 a2
和 a3
的创建在 a1
字段之前,且仅在第一个 B
对象创建时被初始化,之后这些静态对象不会被重新初始化。
因此初始化的顺序是从静态字段开始,然后是非静态字段。例如要执行静态的 main
方法,必须先加载 StaticInitialization
类,然后初始化他的静态字段 b2
,这就导致类 B
被加载,而类 B
中包含静态的类 A
的对象,因此 A
也被加载,所以这个程序中所有的类都在 main
方法开始执行前被加载。
现在总结一下对象创建的过程,假设有一个名为 A
的类:
- 尽管没有显式使用
static
关键字,但构造器实际上也是静态方法。因此,第一次创建类型为A
的对象时,或者第一次访问类A
的静态方法或静态字段时,Java 解释器会搜索类路径来定位A.class
文件。 - 当
A.class
被加载后(这将创建一个 Class 对象),它的所有静态初始化工作都会执行。因此,静态初始化只在 Class 对象首次加载时发生一次。 - 当使用
new A()
创建对象时,构建过程首先会在堆上为A
对象分配足够的存储空间。 - 这块存储空间会被淸空,然后自动将该
A
对象中的所有基本类型设置为其默认值(数值类型的默认值是0,boolean
和char
则是和0等价的对应值),而引用会被设置为null
。 - 执行所有出现在字段定义处的初始化操作。
- 执行构造器。这实际上可能涉及相当多的动作,尤其是在涉及继承时。
3. 显式的静态初始化
Java 允许在一个类里将多个静态初始化语句放在一个特殊的“静态子句”里(有时称为静态块):
package com.yyj;public class ExplicitStatic {public static void main(String[] args) {System.out.println("Inside main()");B.a1.f();/** Inside main()* Initial class A with 1* Initial class A with 2* This is A.f()*/}
}class A {A(int value) {System.out.println("Initial class A with " + value);}void f() {System.out.println("This is A.f()");}
}class B {static A a1;static A a2;static {a1 = new A(1);a2 = new A(2);}B() {System.out.println("Initial class B");}
}
尽管看起来有点像一个方法,但它只是在 static
关键字后加了一段代码,这段代码和其他静态初始化语句一样,只执行一次:第一次创建该类的对象时,或第一次访问该类的静态成员时(即使从未创建过该类的对象)。
更多推荐
Java类中字段初始化过程详解
发布评论