閱讀目錄 屬性初始化和構造器調用 初始化的順序 靜態塊和非靜態塊 初始化的方式 數組初始化 閱讀目錄 閱讀目錄 屬性初始化和構造器調用 初始化的順序 靜態塊和非靜態塊 初始化的方式 數組初始化 屬性初始化和構造器調用 初始化的順序 靜態塊和非靜態塊 初始化的方式 數組初始化 一、屬性初始化和構造器調 ...
閱讀目錄
一、屬性初始化和構造器調用
1)編譯器會為屬性指定一個預設的初始值,例如:int i; 預設初始值為 0, double d; 預設初始值為0.0
2)局部變數使用前必須由程式員指定初始值
3)在構造器被調用之前,屬性就被初始化了
例1:
package com.example; public class Values{ private boolean bt; private char c; private byte b; private short s; private int i; private float f; private double d; private Values v; public void show(){ System.out.println("屬性的預設初始化值:"); System.out.println("boolean: " + bt); System.out.println("char: " + c); System.out.println("byte: " + b); System.out.println("short: " + s); System.out.println("int: " + i); System.out.println("float: " + f); System.out.println("double: " + d); System.out.println("Values: " + v); } }
package com.example; public class Test{ public static void main(String[] args){ Values val = new Values(); val.show(); } }
運行結果為: 屬性的預設初始化值: boolean: false char: byte: 0 short: 0 int: 0 float: 0.0 double: 0.0 Values: null
- 編譯器為所有的屬性都指定了一個預設初始值
- 編譯器為對象的引用指定的預設初始值是 null,例如:例子中的 private Values v;
- 編譯器為 char 類型指定的初始值是0(ASCII 中的 NUL),是空字元,這裡以空白表示
例2:
package com.example; public class Man{ public void speak(){ int i; System.out.println("i = " + i); // error,i 是局部變數,局部變數使用之前必須要初始化 } }
例3:
package com.example; public class Man{ public Man(int i){ System.out.println("Man" + "(" + i + ")"); } }
package com.example; public class Woman{ public Man m1 = new Man(1); public Man m2 = new Man(2); public Woman(){ System.out.println("Woman()"); } public Man m3 = new Man(3); }
package com.example; public class Test{ public static void main(String[] args){ new Woman(); } }
運行結果為: Man(1) Man(2) Man(3) Woman()
- 這個例子不是使用系統的預設值對屬性進行初始化,而是通過我指定的值對屬性進行初始化
- Woman 類中有三個屬性 m1,m2,m3 和一個 構造器 Woman(),從運行結果可以看出:屬性先被初始化,然後構造器才被調用
- 屬性先被初始化這一特性與屬性的位置無關,例如:Woman 類中 m3 就位於構造器的後面,但 m3 也在構造器被調用之前就已經被初始化了
[ 返回頂部 ]
二、初始化的順序
1)屬性分為靜態屬性和非靜態屬性,且靜態屬性比非靜態屬性先初始化,即初始化順序為:靜態屬性 -> 非靜態屬性 -> 構造器調用
2)靜態屬性只會在創建對象或者被直接調用的時候初始化(在主類中靜態屬性也會被初始化),非靜態屬性只會在創建對象的時候初始化
3)無論創建多少個對象,靜態屬性只占用一塊存儲空間,所以靜態屬性只會初始化一次,而非靜態屬性在每次創建對象時都會初始化
4)含有繼承關係的初始化:當創建一個子類對象時,該對象會包含一個父類的對象,那麼保證父類對象初始化的方法是:在子類構造器中調用父類構造器
例1:
package com.example; public class Pear{ public Pear(int i){ System.out.println("Pear" + "(" + i + ")"); } }
package com.example; public class Apple{ public Pear p1 = new Pear(1); public static Pear p2 = new Pear(2); public Apple(){ System.out.println("Apple()"); } public static Pear p3 = new Pear(3); }
package com.example; public class Test{ public static void main(String[] args){ new Apple(); } }
運行結果: Pear(2) Pear(3) Pear(1) Apple()
- 該例子中用 new 創建了 Apple 對象,所以 Apple 類中的靜態屬性和非靜態屬性都會初始化
- 初始化順序:靜態屬性 -> 非靜態屬性 -> 構造器調用,例如:例子中先輸出 Pear(2) 和 Pear(3),然後輸出 Pear(1),最後調用 Apple() 構造器,所以是先初始化靜態屬性 p2 和 p3,然後初始化非靜態屬性 p1,最後調用構造器 Apple()
例2:
package com.example; public class Pear{ public Pear(int i){ System.out.println("Pear" + "(" + i + ")"); } }
package com.example; public class Apple{ public static String color = "red"; public static int number = 10; public String size = "big"; }
package com.example; public class Test{ public static void main(String[] args){ System.out.println("Hello World !"); System.out.println("color: " + Apple.color); } static Pear p1 = new Pear(1); static Pear p2 = new Pear(2); Pear p3 = new Pear(3); }
運行結果: Pear(1) Pear(2) Hello World ! color: red
- Test 類是主類,所以靜態屬性 p1 和 p2 都會初始化。非靜態屬性 p3 不會初始化,是因為我們沒有創建 Test 對象
- Apple 類中有兩個靜態屬性和一個非靜態屬性,該例子中調用了 Apple.color ,靜態屬性 color 在調用的時候會初始化為 red,而靜態屬性 number 和非靜態屬性 size 沒有被調用,也沒有創建對象,所以此時它們不會初始化
例3:
package com.example; public class Cat{ public Cat(int i){ System.out.println("Cat" + "(" + i + ")"); } }
package com.example; public class Dog{ public Cat c1 = new Cat(1); public static Cat c2 = new Cat(2); public Dog(){ System.out.println("Dog()"); } public static Cat c3 = new Cat(3); }
package com.example; public class Test{ public static void main(String[] args){ System.out.println("第一次創建對象"); Dog d1 = new Dog(); System.out.println("第二次創建對象"); Dog d2 = new Dog(); } }
運行結果: 第一次創建對象 Cat(2) Cat(3) Cat(1) Dog() 第二次創建對象 Cat(1) Dog()
- 第一次創建對象的時候,靜態屬性和非靜態屬性都初始化了,但是第二次創建對象的時候,只有非靜態屬性會再次初始化,靜態屬性只初始化一次
例4:
父類不含構造器
package com.example; public class T{ public T(int i){ System.out.println("T" + "(" + i + ")"); } }
package com.example; public class Person{ // 父類 public static T t4 = new T(4); public T t5 = new T(5); public static T t6 = new T(6); }
package com.example; public class Man extends Person{ // 子類 public static T t1 = new T(1); public T t2 = new T(2); public static T t3 = new T(3); public Man(){ System.out.println("Man()"); } }
package com.example; public class Test{ public static void main(String[] args){ new Man(); } }
運行結果: T(4) T(6) T(1) T(3) T(5) T(2) Man()
- 某個類不含構造器時,編譯器會為這個類生成一個預設構造器,該生成的預設構造器是一個無參構造器。這些操作是在編譯時完成的,所以我們看不見
- 當父類含有預設構造器時,編譯器會自動在子類的構造器中調用父類的構造器來對父類進行初始化。這個過程也是在編譯時完成的,所以我們看不見
- 初始化的順序:父類靜態屬性 -> 子類靜態屬性 -> 父類非靜態屬性 -> 子類非靜態屬性 -> 調用子類構造器
父類含有無參構造器
package com.example; public class T{ public T(int i){ System.out.println("T" + "(" + i + ")"); } }
package com.example; public class Person{ // 父類 public static T t4 = new T(4); public T t5 = new T(5); public static T t6 = new T(6); public Person(){ // 無參構造器 System.out.println("Person()"); } }
package com.example; public class Man extends Person{ // 子類 public static T t1 = new T(1); public T t2 = new T(2); public static T t3 = new T(3); public Man(){ System.out.println("Man()"); } }
package com.example; public class Test{ public static void main(String[] args){ new Man(); } }
運行結果: T(4) T(6) T(1) T(3) T(5) Person() T(2) Man()
- 父類含有無參構造器時和父類含有預設構造器時差不多,編譯器會自動在子類的構造器中調用父類的構造器來對父類進行初始化
- 不同的是,當我們創建了構造器之後,編譯器就不會再為類創建一個預設構造器了
- 初始化順序:父類靜態屬性 -> 子類靜態屬性 -> 父類非靜態屬性 -> 父類構造器調用 -> 子類非靜態屬性 -> 子類構造器調用
父類只含有有參構造器
package com.example; public class T{ public T(int i){ System.out.println("T" + "(" + i + ")"); } }
package com.example; public class Person{ // 父類 public static T t4 = new T(4); public T t5 = new T(5); public static T t6 = new T(6); public Person(int i){ System.out.println("Person" + "(" + i + ")"); // 有參構造器 } }
package com.example; public class Man extends Person{ // 子類 public static T t1 = new T(1); public T t2 = new T(2); public static T t3 = new T(3); public Man(){ super(1); // super 關鍵字表示調用父類構造器,且必須位於子類構造器的第一行,每個子類的構造器只能調用父類構造器一次 System.out.println("Man()"); } }
package com.example; public class Test{ public static void main(String[] args){ new Man(); } }
運行結果: T(4) T(6) T(1) T(3) T(5) Person(1) T(2) Man()
- 當父類只有有參構造器時,子類構造器中必須使用 super 關鍵字調用父類構造器,否則會報錯。
- 當子類有多個構造器時,子類中的每個構造器都必須要調用父類構造器(如果父類有多個構造器,那麼子類構造器只需挑父類構造器的其中一個來調用即可,不過一定要保證子類的每個構造器都有調用父類構造器)
父類既含有無參構造器,也含有有參構造器
package com.example; public class T{ public T(int i){ System.out.println("T" + "(" + i + ")"); } }
package com.example; public class Person{ // 父類 public static T t4 = new T(4); public T t5 = new T(5); public static T t6 = new T(6); public Person(){ // 無參構造器 System.out.println("Person()"); } public Person(int i){ System.out.println("Person" + "(" + i + ")"); // 有參構造器 } }
package com.example; public class Man extends Person{ // 子類 public static T t1 = new T(1); public T t2 = new T(2); public static T t3 = new T(3); public Man(){ // 子類無參構造器 System.out.println("Man()"); } public Man(int i){ // 子類有參構造器 System.out.println("Man" + "(" + i + ")"); } }
package com.example; public class Test{ public static void main(String[] args){ System.out.println("調用子類的無參構造器"); new Man(); System.out.println("調用子類的有參構造器"); new Man(1); } }
運行結果: 調用子類的無參構造器 T(4) T(6) T(1) T(3) T(5) Person() T(2) Man() 調用子類的有參構造器 T(5) Person() T(2) Man(1)
- 當父類含有無參構造器時,即使子類有多個構造器,這些構造器也無需顯式地通過 super 關鍵字調用父類構造器,因為編譯器會自動為每個子類的構造器都調用父類的無參構造器,例如:例子中運行結果列印了兩次 Person(),這是兩次創建子類對象時,編譯器自動調用父類中無參構造器的結果
總結:如果要建立繼承關係時,最好給父類創建一個無參構造器或者不給父類創建任何構造器
[ 返回頂部 ]
三、靜態塊和非靜態塊
1)靜態塊會使所有靜態屬性都被初始化,即使靜態屬性不在靜態塊內
例如:
package com.example; public class T{ public T(int i){ System.out.println("T" + "(" + i + ")"); } }
package com.example; public class S{ public static T t1; public static T t2; public static T t3; public T t4; public T t5; public T t6; public T t7 = new T(7); // t7 不在靜態塊內 static{ // 靜態塊 t1 = new T(1); t2 = new T(2); t3 = new T(3); } { // 非靜態塊 t4 = new T(4); t5 = new T(5); t6 = new T(6); } }
package com.example; public class Test{ public static void main(String[] args){ System.out.println(S.t1); } }
運行結果: T(7) T(1) T(2) T(3) com.example.T@15db9742
- 可以看到,該例子中只是調用了 S.t1 ,靜態屬性 t2, t3, t7 也跟著被初始化了
[ 返回頂部 ]
四、初始化的方式
1)在定義處初始化
2)在構造器內初始化
3)在靜態塊或非靜態塊內初始化
4)等到要用的時候再初始化
[ 返回頂部 ]
五、數組初始化
數組有三種初始化的方式
第一種初始化的方式
package com.example; import java.util.Arrays; public class Test{ public static void main(String[] args){ int[] a = {1,2,3,4,5}; /* error, 只能寫在同一行 int[] a; a = {1,2,3,4,5} */ // System.out.println(a); 這種方式無法列印數組的值 System.out.println(Arrays.toString(a)); // 列印數組的值 } }
運行結果: [1, 2, 3, 4, 5]
第二種初始化的方式
package com.example; import java.util.Arrays; public class Test{ public static void main(String[] args){ int[] a; a = new int[5]; for(int i=0; i<5; i++){ a[i] = i; } System.out.println(Arrays.toString(a)); } }
運行結果: [0, 1, 2, 3, 4]
第三種初始化的方式
例1:
package com.example; import java.util.Arrays; public class Test{ public static void main(String[] args){ int[] a; a = new int[]{1,2,3,4,5}; System.out.println(Arrays.toString(a)); } }
運行結果: [1, 2, 3, 4, 5]
例2(這種方式的用途):
package com.example; import java.util.Arrays; public class T{ public void show(Integer[] i){ //必須為 Integer 類型,不能是 int 這些基本類型 System.out.println(Arrays.toString(i)); } public void show(String[] str){ System.out.println(Arrays.toString(str)); } }
package com.example; public class Test{ public static void main(String[] args){ T t1 = new T(); t1.show(new Integer[]{5,4,3,2,1}); // 必須為 Integer 類型,不能是 int 這些基本類型 t1.show(new String[]{"張三", "李四", "王五"}); } }
運行結果: [5, 4, 3, 2, 1] [張三, 李四, 王五]
- 很明顯這種方式的優點是無需額外定義一個數組變數 int[] a
[ 返回頂部 ]
參考資料:
《Java 編程思想》第4版