天天看点

Java之对象构造过程

先来执行一段代码

class A {
	public A() {
		init();
	}
	public void init() {
	}
	public static void main(String[] args) {
		B b = new B();
		System.out.println("最终i的值为:" + b.i + ",j的值为:" + b.j);
	}
}
class B extends A {
	int i;
	int j = 999;
	public void init() {
		System.out.println("此时A的构造方法正在调用此方法:i的值为:" + i + ",j的值为:" + j);
		i = 888;
		j = 111;
	}
}
           

看看打印什么

此时A的构造方法正在调用此方法:i的值为:0,j的值为:0
最终i的值为:888,j的值为:999
           

如果感到非常惊讶,那么你对Java对象的构造过程还不熟悉,那么认真阅读本文将对你有很大帮助。

先写个案例代码

public class Super {
    static long time = 10;
    static Object obj = new Object();
    int width = 100;
    static {
        time = 11;
    }
    {
        width = 110;
    }
    public Super() {
        width = 120;
    }
    public static void main(String[] args) {
        Child child = new Child();
        System.out.println("Super.time:"+Super.time);
        System.out.println("Super.obj:"+Super.obj);
        System.out.println("child.width:"+child.width);
        System.out.println("Child.age:"+Child.age);
        System.out.println("Child.str:"+Child.str);
        System.out.println("child.height:"+child.height);
    }
}
class Child extends Super {
    static int age = 20;
    static String str = "str";
    double height = 200;
    static {
        age = 22;
    }
    {
        height = 210;
    }
    public Child() {
        height = 220;
    }
}
           

打印

Super.time:11
Super.obj:[email protected]
child.width:120
Child.age:22
Child.str:str
child.height:220.0
           

Java中一个对象的构造过程

1.用类加载器加载父类,按父类静态变量定义的顺序的为父类所有静态变量分配空间,并赋予父类静态变量默认值

public class Super {
	static long time=10;//此时time=0
	static Object obj=new Object();//此时obj=null
           

2.用类加载器加载自己,按自己静态变量定义的顺序的为自己所有静态变量分配空间,并赋予自己静态变量默认值

class Child extends Super{
	static int age=20;//此时age=0
	static String str="str";//此时str=null
           

3.按父类静态变量定义的顺序的为父类所有静态变量赋上定义的值

public class Super {
    static long time=10;//此时time=10
    static Object obj=new Object();//此时obj=new Object()
           

4.执行父类静态代码块

public class Super {
	static long time=10;
	static Object obj=new Object();
	int width=100;
	static{
		time=11;//静态代码块执行了,这个时候time=11
	}
           

5.按自己静态变量定义的顺序的为自己所有静态变量赋上定义的值

class Child extends Super{
	static int age=20;//此时age=20
	static String str="str";//此时str="str"
           

6.执行自己静态代码块

class Child extends Super{
	static int age=20;
	static String str="str";
	double height=200;
	static{
		age=22;//此时age=22
	}
           

7.为父类实例变量分配空间,并赋予默认值

public class Super {
	static long time=10;
	static Object obj=new Object();
	int width=100;//此时width=0
           

8.为自己实例变量分配空间,并赋予默认值

class Child extends Super{
	static int age=20;
	static String str="str";
	double height=200;//此时height=0.0
           

9.按父类实例变量定义的顺序的为父类所有实例变量赋上定义的值

public class Super {
	static long time=10;
	static Object obj=new Object();
	int width=100;//此时width=100
           

10.执行父类的构造代码块

public class Super {
	static long time=10;
	static Object obj=new Object();
	int width=100;
	static{
		time=11;
	}
	{
		width=110;//此时width=110
	}
           

11.执行父类的构造方法

public class Super {
	static long time=10;
	static Object obj=new Object();
	int width=100;
	static{
		time=11;
	}
	{
		width=110;
	}
	public Super() {
		width=120;//此时width=120
	}
           

12.按自己实例变量定义的顺序的为自己所有实例变量赋上定义的值

class Child extends Super{
	static int age=20;
	static String str="str";
	double height=200;//此时height=200.0
           

13.执行自己的构造代码块

class Child extends Super{
	static int age=20;
	static String str="str";
	double height=200;
	static{
		age=22;
	}
	{
		height=210;//此时height=210.0
	}
           

14.执行自己的构造方法

class Child extends Super{
	static int age=20;
	static String str="str";
	double height=200;
	static{
		age=22;
	}
	{
		height=210;
	}
	public Child() {
		height=220;//此时height=220.0
	}
           

对象构造完成!

注意

1-6属于初始化静态部分,7-14属于初始化实例部分

如果一个类的静态部分已经初始化了(已经被类加载器加载了),就不会再重复初始化静态部分,静态部分的初始化只会在类加载器加载一个类的时候初始化一次

父类如果还有父类就也依照此顺序先初始化父类的父类,直到Object为止

如果执行步骤3,5,9,12赋值操作时,如果发现所赋的值的类还没有初始化,则会先初始化那个引用的类,如果引用的类还有引用的类则也按照此顺序先初始化引用类的引用类,直到所有被引用的类全部被初始化完毕为止

例如:

我们在A类中定义一个B类的引用。

public class Super {
	public static void main(String[] args) {
		new A();new B();
	}
}
class A{
	static B b=new B();//这句代码会导致B类会比A类先初始化完成,也就是说B的静态属性会先赋值,静态代码块会先执行。
	static {
		System.out.println("AA");
	}
}
class B{
	static {
		System.out.println("BB");
	}
}
           

打印:

BB
AA
           

只定义一个类的引用,而没有赋值,那么不会触发一个类初始化

public class Super {
	public static void main(String[] args) {
		new A();
	}
}
class A{
	static B b;
	static {
		System.out.println("AA");
	}
}
class B{
	static {
		System.out.println("BB");
	}
}
           

打印:

AA
           

只有触发了主动使用才会导致所引用的类被初始化.

关于一个人在什么情况才算是主动使用请查看我的另一篇文章:

http://blog.csdn.net/u012643122/article/details/46522345

如果一个类A的所引用的类B里又引用了类A,也就是递归引用的情况,那么会实施java消除递归机制

public class Super {
	public static void main(String[] args) {
		new A();
	}
}
class A{
	static B b=new B();
	static {
		System.out.println("AA");
	}
}
class B{
	static A a=new A();
	static {
		System.out.println("BB");
	}
}
           

打印

BB
AA
           

初始化引用的类时就像走一条路,java避免递归的机制就是不走之前已经走过的地方.

如果在执行3、5、9、12时,发现变量只定义了引用而没有赋值操作,那么该变量将保持默认值

如:

static long time;//保持之前所赋的默认值0
Child child;//保持之前所赋的默认值null
           

特殊情况可省略的步骤

如果一个类没有父类(如Object类),则它的初始化顺序可以简化成2、5、6、8、12、13、14。

如果这个类已经被类加载器加载过了,也就是该类的静态部分已经初始化过了,那么1、2、3、4、5、6都不会执行,总的顺序可以简化为7、8、9、10、11、12、13、14。

如果这个类没有被类加载器加载,但它的父类已经被类加载器加载过了,那么总的顺序可以简化为2、5、6、7、8、9、10、11、12、13、14。

转载请标明原地址,请尊重原创,谢谢!