单一对象的初始化过程
△概念
所谓对象的初始化过程,就是当你new了一个对象之后,对象所在类的各个成分执行顺序。
△类的成分,各个成分的作用
→成员变量:分为静态成员变量以及普通成员变量。
→成员方法:如果仅仅只是new了一个对象,不会调用成员方法,除非构造方法里面调用成员方法,这个不在这里论述。
→代码块:分为静态代码块以及构造代码块。静态代码块是用来初始化类,当在程序里面首次使用一个类时,会执行他,第二次再使用一个类时,不执行他。构造代码块用来初始化对象,而且是初始化所有对象,每次创建对象都会执行
→构造方法:用来对对象做初始化,可能会有多个不同重载方法。
△示例程序
/**
演示对象的初始化过程
*/
public class My
{
public static void main(String[] args)
{
System.out.println("Hello World!");
Person p1 = new Person();
// Person p2 = new Person(4);
}
}
class Person
{
private String name = "haha";
private int age = 4;
private static String country = "China";
static{
System.out.println("静态代码块初始化"+"conutry=="+country);
}
{
System.out.println("构造代码块初始化--"+"name=="+name+"--age=="+age);
}
Person(){
System.out.println("无参构造器初始化");
}
Person(int a){
System.out.println("有参构造其初始化");
}
}
命令行的打印结果:
由此可得,当程序执行new Person();对象初始化的过程如下:
1,静态成员变量的初始化。
2,静态代码块的执行。
3,成员变量的初始化(你看到我打印出了”haha”和”4”,说明成员变量已经初始化了,如果没有做初始化,应该打印null和0).
4,构造代码块的执行。
5,指定构造器初始化。
△对象初始化的规律
1:先对类做了初始化,才对对象做初始化。所以静态成员变量比非静态优先做初始化,静态代码块优先于构造代码块的执行。
2:先统一做初始化,在具体到每个对象作初始化。什么意思呢?构造代码块是对所有的对象做初始化用的,构造方法是由类的使用者所特指,所以,构造代码块优先于构造函数执行。就是先统一在具体。
注:为什么成员变量优先于构造代码块初始化,因为构造代码块是对所有的对象做初始化,要对对象做初始化对象就要现有属性,所以,先要加载对象属性,才能执行构造代码块,所以成员变量优先,同样道理,静态成员变量也优先于静态的代码块。
△单一对象的初始化过程总结
根据上述演示过程以及规律,总结对象的初始化过程:
→加载类(先有类才能够创建对象),不过加载类这么个行为我们可是看不到的。
→静态成员变量的出始化。
→静态代码块的执行(为了对类做初始化)。
→堆内存里开辟空间(对象放在堆内存里)
→非静态成员变量初始化。
→构造代码块对对象做初始化(所有对象都要执行构造代码块的内容)。
→执行指定的构造方法。
构造方法执行完成之后,对象正式创建完毕。这里你也可以看出,各个对象前面几步是一样的,只有构造方法的执行是不一样的。
继承体系下的对象的初始化过程
△概述
简而言之就是加多了一个类,有了上面那些讲述,继承体系下的对象的初始化相对来讲也并不难,还是先看示例再来分析原因。
△示例代码
import java.io.*;
public class My
{
public static void main(String[] args) throws Exception{
Person p = new Work();
}
}
class Person
{
String name = "lao wang";
int age = 40;
static{
System.out.println("Person static");
}
{
System.out.println("Person 构造代码块的执行");
}
public Person(){
System.out.println("执行Person无参的构造器");
System.out.println("Person.name = "+this.name+"--"+"Person.age = "+this.age);
}
}
class Work extends Person
{
int salary = 4000;
static{
System.out.println("Work static");
}
{
System.out.println("Work 构造代码块的执行");
}
public Work(){
System.out.println("执行Work无参的构造器");
}
}
运行结果:
为什么是上述的结果呢:
继承体系下对象的初始化是这么一个过程:
1,首先,仍然是要先加载类,也就是要对类做初始化,很自然的,先要加载的是超类,然后才要加载子类。
2,当两个类加载完了,就要开始创建对象,先要创建超类对象,然后创建子类对象。
所以我们看到这个打印结果:
超类静态代码块的执行→子类静态代码块的执行(到这一步两个类的类都加载完毕)→超类构造代码块的执行→超类构造方法执行(超类对象创建完毕)→子类构造代码块的执行→子类构造器的执行(到此子类创建完毕)
△继承体系下对象的创建过程小结
结合开头所讲到的一个类的初始化的过程,总结继承体系下的对象创建过程:
→加载超类:初始化超类的静态成员变量,执行超类的静态代码块。
→加载子类:初始化子类的静态成员变量,执行子类的静态代码块。
→创建超类的对象:初始化超类成员变量,执行超类构造代码块及构造函数。
毫无疑问超类静态成员的初始化是最先的,这里需要注意的一点是,先初始化子类静态成员变量,才会加载超类对象,并 不是将超类对象初始化完,才会开始加载子类。其实这个也很自然,因为“子类构造方法里面,默认会去调用超类无参构造方法”所以当然先要加载子类,才会开始创建超类对象。→创建子类的对象:初始化子类成员变量,执行子类构造代码块和构造函数。