1.类的生命周期
java虚拟机通过装载、连接、初始化一个java类,使其能够被程序实例化。如下图所示:
其中,装载和连接在初始化之前就已经完成。
2.类的初始化
类在初始化阶段主要是为类的变量赋值,通过类变量初始化语句或静态初始化语句,为变量赋值。(初始化阶段不执行非静态代码、构造器函数),静态代码块,静态变量,按声明的顺序执行。
类的初始化包含两个步骤:
(1)如果类继承了父类,并且父类没有被初始化,则先初始化父类。
(2)如果类中有初始化方法,则执行初始化方法。
3.类初始化的时机
类什么时候进行初始化呢? java虚拟机规范对其作出定义:首次主动使用时进行初始化。
包含6中类型,分别是:
(1)创建类的新实例(new,反射,克隆或反序列化)
(2)调用类的静态方法
(3)操作类或接口的静态字段(final字段除外)
(4)调用java的特定的反射方法
(5)初始化一个类的子类
(6)指定一个类作为java虚拟机启动的初始化类(含有main方法的启动类)
当一个类初始化完成后,就可以被使用了。
4.对象实例化
对象创建时,会初始化对象的属性,执行声明的非静态变量,非静态代码块,构造函数。执行顺序为:按代码的顺序 执行声明的非静态变量和非静态代码块,最后执行构造函数。
class Super {
{
a = "2";
}
String a = "1";
public static void main(String[] args) {
System.out.println(new Super().a);
}
}
输出结果为1,按代码顺序先执行代码块,后执行变量a的声明。当对象继承父类时,先按顺序实例化父类,在实例化子类。
5.案例
5.1 案例1
public class initTester {
public initTester() {
System.out.println("parent");
}
static {
System.out.println("static parent");
}
public static void main(String[] args) {
System.out.println("main");
}
}
指定一个类作为Java虚拟机启动时的初始化类
在运行main方法时,需要初始化initTester,调用static代码块,输出static parent
initTester初始化完成后,运行main方法,输出main
5.2 案例2
public class InitField {
public static void main(String[] args) {
SuperInitField p = new SuperInitField();
SuperInitField c = new SubInitField();
}
}
class SuperInitField {
public SuperInitField() {
System.out.println("parent");
}
static {
System.out.println("static parent");
}
}
class SubInitField extends SuperInitField {
public SubInitField() {
System.out.println("child");
}
static {
System.out.println("static child");
}
}
当执行SuperInitField p = new SuperInitField() 时,
(1)SuperInitField的超类是Object,先初始化Object
(2)创建SuperInitField对象,属于首次主动使用,因此要先初始化Object类,然后再调用SuperInitField类变量初始化语句或者静态初始化语句,所以要输出static parent
(3)类被装载、连接和初始化之后,创建一个对象,因此需要首先调用了Object的默认构造方法,然后再调用自己的构造方法,所以要输出parent
当执行SuperInitField c = new SubInitField()时
(1)SubInitField继承自SuperInitField
(2)创建SubInitField对象,属于首次主动使用,父类SuperInitField已被初始化,因此只要调用SubInitField类变量初始化语句或者静态初始化语句,所以要输出static child
(3)类被装载、连接和初始化之后,创建一个对象,因此需要首先调用了SuperInitField的构造方法,然后再调用自己的构造方法,所以要输出parent,然后再输出child