1.当不含static成员时,先看实例:
如图所示,我们先定义相关类,Building, House(继承Building), Villa(继承House), 同时,House有成员变量LivingRoom, LivingRoom有成员变量Bed和Desk。具体如下代码:
//建筑
class Building {
public Building() {
System.out.println("Building");
}
}
//房子
class House extends Building{
public LivingRoom livingRoom = new LivingRoom();//卧室
public House() {
System.out.println("House");
}
}
//别墅
class Villa extends House{
public Villa () {
System.out.println("Villa");
}
}
//卧室
class LivingRoom {
public Bed bedFirst = new Bed();//床
public Desk deskFirst = new Desk();//桌子
public LivingRoom () {
System.out.println("LivingRoom");
}
}
//床
class Bed {
public Bed () {
System.out.println("Bed");
}
}
//桌子
class Desk {
public Desk () {
System.out.println("Desk");
}
}
此时,我们new Villa():
public class InitializeDemo {
@Test
public void testInitialize() {
Villa villa = new Villa();
}
}
执行结果为:
Building
Bed
Desk
LivingRoom
House
Villa
解析:
1.1 我们new一个对象A时,首先会先创建A类的父类B的实例对象,如果B类仍有父类C,会先创建父类C的对象,以此类推,是一个递归的创建过程;
1.2 当该类的父类对象已经全部创建并初始化成功时,会对该类进行创建和初始化。但是,在对该类进行初始化时,会先初始该类对象的成员变量,再执行该类的构造方法。
1.3 成员变量的初始化和代码块的执行顺序,是由它们的声明顺序决定的,按顺序依次初始化或执行,但均在构造器方法之前执行。
在此例中,我们在创建Villa对象时,会先试着创建Villa的父类House的对象,但House也有父类Building, Building还有父类Object。因此,实际上,本例中会先创建Object对象并初始化,在Object对象创建并初始化成功后,会创建Building对象,Building对象没有成员变量,所以直接执行构造器,打印出Building。
接着,开始创建House对象,House对象有成员变量livingRoom,因此,会先初始化livingRoom,创建LivingRoom时,也会先创建LivingRoom的父类Object对象并初始化,其次再创建LivingRoom实例,LivingRoom同样有成员变量Bed和Desk,因而会按两个成员变量的定义顺序,先后创建Bed和Desk对象并初始化,先后打印出了Bed和Desk。初始化成员变量成功后,执行LivingRoom的构造器,打印LivingRoom。 初始化LivingRoom成功后,开始执行House的构造器,打印House。
最后,在House初始化成功后,执行了Villa的构造器,打印出Villa。初始化成功。
2. 当有static成员时
2.1 静态成员变量只会被初始化一次,静态化代码块只会被执行一次,在该类第一次被JVM加载时
2.2 会先加载父类字节码,再加载子类字节码,如果有创建某类的实例对象,也是在该类的父类和该类的字节码加载完成,即静态成员初始化或执行完成之后,才会实例化该类父类的实例对象和该类的实例对象
2.3 静态成员变量的初始化和静态化代码块的执行顺序,是由它们的声明顺序决定的,按顺序依次初始化或执行
class Family {
Family (Class className) {
System.out.println("创建Family对象");
System.out.println(className.getName());
}
}
class Person {
Person() {
System.out.println("创建Person对象");
}
public static Family family = new Family(Person.class);
static {
System.out.println("执行Person静态代码块");
}
{
System.out.println("执行Person代码块");
}
}
public class Student extends Person {
public Student() {
System.out.println("创建student对象");
}
{
System.out.println("执行Student代码块");
}
static {
System.out.println("执行Student静态代码块");
}
public static Family staticFamily = new Family(Student.class);
@Test
public void test() {
System.out.println("=============");
}
public Family family = new Family(Student.class);
}
执行结果:
创建Family对象
com.tca.thinkInJava.chap7.Person
执行Person静态代码块
执行Student静态代码块
创建Family对象
com.tca.thinkInJava.chap7.Student
执行Person代码块
创建Person对象
执行Student代码块
创建Family对象
com.tca.thinkInJava.chap7.Student
创建student对象
=============
分析:
1. 首先在执行@Test方法时,必须创建Student的实例对象
2. 要创建Student的实例对象,第一步会先加载Student父类Person和Student类的字节码,并对相关静态成员变量进行初始化,执行静态代码块。
3. 首先加载父类Person字节码,静态成员变量public static Family family优于静态代码块前声明,所以先对静态成员变量family进行初始化。创建Famlily对象,打印“创建Family对象", "com.tca.thinkInJava.chap7.Person"。再执行静态代码块,打印"执行Person静态代码块"。
4. 加载完Person字节码后,再加载Student字节码。静态代码块在静态成员public static Family staticFamily之前声明,所以先执行静态代码块,打印"执行Student静态代码块",再进行成员变量初始化,执行"创建Family对象", "com.tca.thinkInJava.chap7.Student"。Student字节码加载完毕。
5. 创建Student父类Person的实例对象,创建实例对象时,先进行非静态成员变量初始化和代码块的执行,再执行构造器方法。所以先打印"执行Person代码块", 再打印"创建Person对象"。父类对象创建和初始化完毕。
6. 创建子类对象Student,先进行非静态成员变量初始化和代码块的执行,再执行构造器方法。代码块的声明在非静态成员变量public Family family声明之前,所以先执行代码块, 打印"执行Student代码块", 再对family进行初始化,创建Family对象,打印"创建Family对象", "com.tca.thinkInJava.chap7.Student",最后执行构造器,打印"创建student对象"。子类对象创建和初始化完毕。
7. 执行test测试方法