Java对象初始化的过程:
代码块,实例变量的初始化,构造函数赋值。
1,静态代码块,静态变量的赋值,按出现的顺序运行
2,实例变量的赋值,代码块的初始化,按出现的顺序运行
3,构造函数初始化
含继承关系的对象实例化的顺序:
父类的类构造器<clinit>() -> 子类的类构造器<clinit>() ->
父类的成员变量和实例代码块 -> 父类的构造函数 -> 子类的成员变量和实例代码块 -> 子类的构造函数。
类初始化和实例初始化的过程:
类加载的过程:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using) 和 卸载(Unloading)七个阶段
(1) 通过子类引用父类的静态字段,不会导致子类初始化,但是其父类会被加载,但是不会创建实例对象,只会加载类。
对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。在本例中,由于value字段是在类SClass中定义的,因此该类会被初始化;此外,在初始化类SClass时,虚拟机会发现其父类SSClass还未被初始化,因此虚拟机将先初始化父类SSClass,然后初始化子类SClass,而SubClass始终不会被初始化。
(2) 通过数组定义来引用类,不会触发此类的初始化
(3) 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
类加载过程:
1、加载(Loading)
2、验证(Verification)
3、准备(Preparation)
4、解析(Resolution)
准备阶段是正式为类变量(static 成员变量)分配内存并设置类变量初始值(零值)的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中。
5、初始化(Initialization)
初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块static{}中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,
静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问
。
6、使用
7、卸载
典型案例分析:
在Java中, 创建一个对象常常需要经历如下几个过程:父类的类构造器<clinit>() -> 子类的类构造器<clinit>() -> 父类的成员变量和实例代码块 -> 父类的构造函数 -> 子类的成员变量和实例代码块 -> 子类的构造函数。
实例初始化不一定要在类初始化结束之后才开始初始化,实例初始化早于类初始化,先对实例初始化,然后按顺序执行静态代码块。
如果类变量是final的,那么编译器在编译时就会为value生成ConstantValue属性,并在准备阶段虚拟机就会根据ConstantValue的设置将变量设置为指定的值。
package
Day01;
public
class
StaticTest {
public
static
void
main(String[]
args
) {
staticFunction
();
}
static
StaticTest
st
=
new
StaticTest();
static
{
//静态代码块
System.out.println("1");
}
{
// 实例代码块
System.out.println("2");
}
StaticTest() {
// 实例构造器
System.out.println("3");
System.
out
.println(
"a="
+
a
+
",b="
+
b
);
}
public
static
void
staticFunction() {
// 静态方法
System.out.println("4");
}
int
a
= 110;
// 实例变量
static
int
b
= 112;
// 静态变量
static
StaticTest
st1
=
new
StaticTest();
}/* Output:
2
3
a=110,b=0
1
2
3
a=110,b=112
4
*///:~
class
Foo {
int
i
= 1;
Foo() {
System.out.println(i);
int
x
= getValue();
System.out.println(x);
}
{
i
= 2;
}
protected
int
getValue() {
return
i
;
}
}
//子类
class
Bar
extends
Foo {
int
j
= 1;
Bar() {
j
= 2;
}
{
j
= 3;
}
@Override
protected
int
getValue() {
return
j
;
}
}
public
class
ConstructorExample {
public
static
void
main(String...
args
) {
Bar
bar
=
new
Bar();
System.out.println(bar.getValue());
}
}
由于Bar重载了Foo中的getValue方法,所以根据Java的多态特性可以知道,其调用的getValue方法是被Bar重载的那个getValue方法。
参考: