天天看點

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。

轉載請标明原位址,請尊重原創,謝謝!