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:java.lang.Object@659e0bfd
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。




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



posted @ 2017-04-15 19:22  gccbuaa  阅读(390)  评论(0编辑  收藏  举报