一個類的全部基本數據成員都會獲得初始化,運行下面的例子能夠查看這些默認值:ide
class Default{ boolean t; char c; byte b; short s; int i; long l; float f; double d; public void show() { System.out.println("基本類型 初始化值\n"+ "boolean<----->" + t +"\n" + "char<----->" + c +"\n" + "byte<----->" + b + "\n" + "short<----->" + s + "\n" + "int<----->" + i + "\n" + "long<----->" + l + "\n" + "float<----->" + f + "\n" + "double<----->" + d + "\n" ); } } public class InitValue { public static void main(String[] args) { Default d = new Default(); d.show(); } }
【運行結果】:函數
基本類型 初始化值 boolean<----->false char<-----> byte<----->0 short<----->0 int<----->0 long<----->0 float<----->0.0 double<----->0.0
其中,char類型的默認值爲空(null)。編碼
對於非基本數據類型而言,對象的句柄也會被初始化:spa
class Person { private String name; // setter } class Default { Person p; public void show() { System.out.println("Person<----->" + p); } } public class InitValue { public static void main(String[] args) { Default d = new Default(); d.show(); } }
【運行結果】:code
Person<----->null
可見,句柄初始化值爲null。這就是說,若是沒有爲p指定初始化值就調用相似於p.setName
的方法,就會出現異常。對象
若是須要本身爲變量賦一個初始值,能夠在定義變量的同時賦值。blog
class Default{ boolean t = true; char c = 'A'; byte b = 47; short s = 0xff; int i = 24; long l = 999; float f = 1.2f; double d = 1.732; public void show() { System.out.println( "boolean<----->" + t +"\n" + "char<----->" + c +"\n" + "byte<----->" + b + "\n" + "short<----->" + s + "\n" + "int<----->" + i + "\n" + "long<----->" + l + "\n" + "float<----->" + f + "\n" + "double<----->" + d + "\n" ); } } public class InitValue { public static void main(String[] args) { Default d = new Default(); d.show(); } }
甚至能夠經過一個方法來進行初始化;繼承
class Person { int i = set(); //... }
這些方法也可使用自變量:ci
class Person { int i; int j = set(i); //... }
構建器進行初始化的優勢是能夠在運行期決定初始化值。例如:it
class Person { int age; Person() { age = 89; } }
age首先會初始化爲0,而後變成89。對於全部基本類型以及對象的句柄,這種狀況都是成立的。
在一個類裏,初始化的順序是由變量在類內的定義順序決定的。即便變量定義大量遍及於方法定義的中間,那麼變量仍然會在調用任何方法(包括構造函數)以前獲得初始化。例如:
class Pet { Pet(int age) { System.out.println("Pet(" + age + ")"); } } class Person { Pet t1 = new Pet(1); Person() { System.out.println("---Person()---"); t3 = new Pet(33); } Pet t2 = new Pet(2); void show() { System.out.println("show----running"); } Pet t3 = new Pet(3); } public class OrderOfInitialization { public static void main(String[] args) { Person p = new Person(); p.show(); } }
【運行結果】:
Pet(1) Pet(2) Pet(3) ---Person()--- Pet(33) show----running
上例中,雖然t一、t二、t3的定義遍及於類中,可是初始化的前後順序是由t一、t二、t3的定義順序決定的(本身動手調換t一、t二、t3看看結果),且初始化優先於構建器執行,當調用Person的構建器時,t3從新初始化。
若是數據是靜態的(static),一樣的過程也會執行。若屬於基本類型,並且未對其進行初始化,就會自動得到本身的標準基本類型初始值;若它是指向一個對象的句柄,除非建立一個對象同它鏈接起來,不然獲得一個空值(null)。若是在定義時初始化,採起的方式與非靜態值是不一樣的,這是由於static只有一個存儲區域。例如:
class Bowl { Bowl(int marker) { System.out.println("Bowl(" + marker + ")"); } void f(int marker) { System.out.println("f(" + marker + ")"); } } class Table { static Bowl b1 = new Bowl(1); Table() { System.out.println("Table()"); b2.f(1); } void f2(int marker) { System.out.println("f2(" + marker + ")"); } static Bowl b2 = new Bowl(2); } class Cupboard { Bowl b3 = new Bowl(3); static Bowl b4 = new Bowl(4); Cupboard() { System.out.println("Cupboard()"); b4.f(2); } void f3 (int marker) { System.out.println("f3(" + marker + ")"); } static Bowl b5 = new Bowl(5); } public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new Cupboard() in main"); new Cupboard(); System.out.println("Creating new Cupboard() in main"); new Cupboard(); t2.f2(1); t3.f3(1); } static Table t2 = new Table(); static Cupboard t3 = new Cupboard(); }
【運行結果】:
Bowl(1) Bowl(2) Table() f(1) Bowl(4) Bowl(5) Bowl(3) Cupboard() f(2) Creating new Cupboard() in main Bowl(3) Cupboard() f(2) Creating new Cupboard() in main Bowl(3) Cupboard() f(2) f2(1) f3(1)
Java容許將其餘static初始化工做劃分到類內一個特殊的代碼塊中,這種代碼塊的形式爲static關鍵字,後面跟着一個方法主體,稱爲靜態代碼塊。靜態代碼塊只有在第一次生成那個類的對象或首次訪問屬於那個類的static成員時執行。例如:
class Person { Person(int age) { System.out.println("Person(" + age + ")"); } void f(int age) { System.out.println("f(" + age + ")"); } } class Persons { static Person p1; static Person p2; static { p1 = new Person(1); p2 = new Person(2); } Persons() { System.out.println("Persons()"); } } public class ExplicitStatic { public static void main(String[] args) { System.out.println("Inside main()"); Persons.p1.f(18);//1 } static Persons x = new Persons();//2 static Persons y = new Persons();//2 }
在標記爲1的行內訪問static對象p1的時候,或在行1被註釋而行2未被註釋是,用於Persons的static初始化模塊就會運行。若1和2都被註釋掉,則用於Persons的靜態代碼塊不會執行。
class Person { Person(int age) { System.out.println("Person("+age+")"); } } class Persons { static Person p = new Person(2); // 1 static { p = new Person(3); } static Person p = new Person(2); // 2 } public class CompStaticInit { public static void main(String[] args) { } static Persons x = new Persons(); }
根據註釋1保留2,註釋2保留1的結果分析可知,靜態屬性和靜態代碼塊的執行順序取決於編碼的順序。誰在前面就先執行誰。
class Animal { Animal(int age) { System.out.println("Animal(" + age + ")"); } void f(int age) { System.out.println("f(" + age + ")"); } } public class NotStaticInit { Animal a1; Animal a2; { a1 = new Animal(1); a2 = new Animal(2); System.out.println("a1 & a2 initialized"); } NotStaticInit() { System.out.println("NotStaticInit"); } public static void main(String[] args) { System.out.println("Inside main()"); NotStaticInit x = new NotStaticInit(); } }
相似於靜態代碼塊,匿名代碼塊與非靜態屬性的初始化順序取決於編碼順序。
class Insect { int i = 1; int j; Insect() { prt("i = " + i + ", j = " + j); j = 2; } static int x1 = prt("static Insect.x1 initialized"); static int prt(String s) { System.out.println(s); return 3; } } public class Beetle extends Insect { int k = prt("Beeklt.k initialized"); Beetle() { prt("k = " + k); prt("j = " + j); } static int x2 = prt("static Bootle.x2 initialized"); static int prt(String s) { System.out.println(s); return 4; } public static void main(String[] args) { prt("Beetle constructor"); Beetle b = new Beetle(); } }
【運行結果】:
static Insect.x1 initialized static Bootle.x2 initialized Beetle constructor i = 1, j = 0 Beeklt.k initialized k = 4 j = 2
對Beetle運行Java時,發生的第一件事情是裝載程序到外面找到那個類。在裝載過程當中,裝載程序發現一個基礎類,因此隨之將其載入。不管是否生成基礎類的對象,這一過程都將執行。若是基礎類含有另外一個基礎類,則另外一個基礎類隨即也會載入,以此類推。接下來就在根基礎類中執行static初始化,再在下一個衍生類中執行,以此類推。這是由於衍生類的初始化可能要依賴於對基礎類成員的初始化。
當類都裝載完畢,就能建立對象。首先,這個對象中的全部基本數據類型都會設置成爲他們的默認值,對象句柄設爲null。而後執行基礎類的構建器。這種狀況是自動完成的(衍生類的構造函數中默認調用了super()
,也能夠經過super指定基類的構建器)。基礎類構建器完成後,衍生類實例變量就會按原本的順序獲得初始化,而後執行構建器的剩餘的主體部分。
總結對象建立的過程:
- 靜態只在類加載的時候執行且只執行一次;
- 非靜態只有在實例化的時候執行,每次建立對象都執行;
- 靜態在非靜態以前執行,基類靜態優先於衍生類靜態執行;
- 靜態屬性和靜態代碼塊的執行屬性取決於它們在類中的位置,誰在前先執行誰;
- 非靜態屬性和構造塊的執行順序取決於它們在類中的位置,誰在前執行誰。