閱讀目錄 作用 super 關鍵字 訪問控制許可權 初始化順序 @Override 與方法重寫 繼承抽象類 final 關鍵字 閱讀目錄 閱讀目錄 作用 super 關鍵字 訪問控制許可權 初始化順序 @Override 與方法重寫 繼承抽象類 final 關鍵字 作用 super 關鍵字 訪問控制許可權 ...
閱讀目錄
一、作用
1)復用類
[ 返回頂部 ]
二、super 關鍵字
1)第一種用法:super 關鍵字是父類對象的引用
package com.example; public class Person{ // 父類 public String name="張三"; public int age = 20; public void speak(){ System.out.println("Person: speak()"); } public void cry(){ System.out.println("Person: cry()"); } }
package com.example; public class Man extends Person{ // 子類 public void speak(){ System.out.println("Man: speak()"); super.speak(); // 調用父類的 speak cry(); // 調用父類的 cry System.out.println("name: " + name); // 調用父類的 name System.out.println("age: " + super.age); // 調用父類的 age } }
package com.example; public class Test{ public static void main(String[] args){ Man m = new Man(); m.speak(); } }
運行結果: Man: speak() Person: speak() Person: cry() name: 張三 age: 20
- 父類和子類中都有 speak 方法,所以子類中想要調用父類的 speak 方法時,必須要用 super 關鍵字(這種情況是子類對父類的方法進行了重寫)
- 其他情況可以使用 super 關鍵字也可以不使用,例如:例子中的 cry(), name, super.age
- 其實在創建子類對象的時候,父類對象會先被創建,而且父類對象被包裹在子類對象中(可以想象成在 Man 對象中有 Person super = new Person() 這樣的代碼行,這裡的 super 就是父類對象的引用)
2)第二種用法:super() 或者 super(參數列表) 是父類的構造器
例1:
package com.example; public class Person{ // 父類 public Person(){ System.out.println("Person()"); } }
package com.example; public class Man extends Person{ // 子類 public Man(){ super(); // 父類構造器 System.out.println("Man()"); // super(); //error } }
package com.example; public class Test{ public static void main(String[] args){ new Man(); } }
運行結果: Person() Man()
- 父類構造器只能在子類構造器中調用,不能在子類的方法中調用
- 父類構造器只能位於子類構造器的第一行
- 每個子類構造器只能調用一個父類構造器
例2:
package com.example; public class Person{ // 父類 public Person(int i){ System.out.println("Person" + "(" + i + ")"); } public Person(String str){ System.out.println("Person" + "(" + str + ")"); } }
package com.example; public class Man extends Person{ // 子類 public Man(){ super(1); // 調用父類構造器 System.out.println("Man()"); } public Man(int i){ super("張三"); // 調用父類構造器 System.out.println("Man" + "(" + i + ")"); } public Man(String str){ super(2); // 調用父類構造器 System.out.println("Man" + "(" + str + ")"); } }
package com.example; public class Test{ public static void main(String[] args){ new Man(); } }
運行結果: Person(1) Man()
- 如果我們沒有給類創建構造器,編譯器會為該類自動創建一個預設構造器(無參構造器),該操作在編譯時完成,我們看不見。如果我們給類創建了構造器,那麼編譯器便不會再為類創建預設構造器
- 當父類中含有預設構造器時,編譯器會在子類的構造器中自動調用父類的預設構造器,以完成父類的初始化,該操作也是在編譯時完成的,我們看不見
- 當父類中含有無參構造器時,編譯器也會在子類構造器中自動調用父類的無參構造器
- 當父類中只含有有參構造器時,編譯器不會在子類構造器中自動調用父類的構造器,此時我們必須在子類的所有構造器中都對父類中的一個構造器進行調用,否則編譯時會報錯,例如:例子中子類 Man 中的每一個構造器都調用了父類的構造器
- 總結:在繼承關係中,父類最好不創建構造器或者在創建多個構造器時要創建一個無參構造器,這樣才不用每個子類的構造器都對父類的構造器進行調用
[ 返回頂部 ]
三、訪問控制許可權
1)Java 的訪問控制許可權有 public,protected,<default>,private 其中如果沒有涉及繼承關係時,protected 和 <default> 都只能在包內訪問
package net.example; public class S{ public int i = 10; protected double d = 51.2; String str1 = "張三"; // <default> private String str2 = "李四"; }
package com.example; public class T{ public int i = 10; protected double d = 51.2; String str1 = "張三"; // <default> private String str2 = "李四"; }
package com.example; import net.example.S; // 導入 S 類 public class Test{ public static void main(String[] args){ S s1 = new S(); System.out.println("S: " + s1.i); //System.out.println("S: " + s1.d); // error //System.out.println("S: " + s1.str1); // error //System.out.println("S: " + s1.str2); // error T t1 = new T(); System.out.println("T: " + t1.i); System.out.println("T: " + t1.d); System.out.println("T: " + t1.str1); //System.out.println("T: " + t1.str2); // error } }
運行結果: S: 10 T: 10 T: 51.2 T: 張三
- private 可以跨包訪問
- 沒有繼承關係時,protected 和 <default> 都只能在包內訪問。例子中 Test 和 T 在同一個包,Test 和 S 在不同的包,所以 s1.d 和 s1.str1 都不能通過編譯,而 t1.d 和 t1.str1 可以通過編譯
- private 只能在同一個類中被訪問
2)子類可以訪問父類中用 protected 限制的屬性和方法,即使子類和父類不在同一個包內
package net.example; public class Animals{ // 父類 protected int age = 2; String name = "旺財"; // <default> protected void bark(){ System.out.println("汪汪"); } }
package com.example; import net.example.Animals; // 導入 Animals 類 public class Dogs extends Animals{ // 子類 public Dogs(){ System.out.println(age); //System.out.println(name); // error bark(); } }
package com.example; public class Test{ public static void main(String[] args){ new Dogs(); } }
運行結果: 2 汪汪
- 父類中的 name 屬性的訪問控制許可權為 <default> ,所以子類不能調用該屬性
- protected 訪問控制許可權可以保證只有繼承者才能對父類的屬性和方法進行訪問,當然這是在跨包的情況下,如果某一個類和父類在同一個包,就算這個類不繼承父類,那麼也可以隨意訪問父類中由 protected 限制的屬性和方法
package com.example; public class Person{ // 父類 public void p1(){ System.out.println("Person: p1"); } protected void p2(){ System.out.println("Person: p2"); } void p3(){ System.out.println("Person: p3"); } private void p4(){ System.out.println("Person: p4"); } }
package com.example; public class Man extends Person{ // 子類 public void p1(){ System.out.println("Man: p1"); } /* public void p2(){ System.out.println("Man: p2"); } */ protected void p2(){ System.out.println("Man: p2"); } /* void p2(){ // error, protected 的訪問控制許可權比 <default> 的高 System.out.println("Man: p2"); } */ /* public void p3(){ System.out.println("Man: p3"); } protected void p3(){ System.out.println("Man: p3"); } */ void p3(){ System.out.println("Man: p3"); } /* public void p4(){ System.out.println("Man: p4"); } protected void p4(){ System.out.println("Man: p4"); } void p4(){ System.out.println("Man: p4"); } */ private void p4(){ System.out.println("Man: p4"); } }
package com.example; public class Test{ public static void main(String[] args){ Man m = new Man(); m.p1(); m.p2(); m.p3(); //m.p4(); // error } }
運行結果: Man: p1 Man: p2 Man: p3
- 子類 Man 中的 p2 方法不能用 <default> 訪問控制許可權進行重寫,因為 protected 的控制許可權比較高
- Test 類中不能調用 m.p4() 因為 p4() 的訪問控制許可權是 private,只能在類的內部進行調用
- 子類重寫父類的方法時,只要重寫方法的訪問控制許可權比父類中的高就行,比如:父類中的 p2 方法由 protected 限制,所以子類 Man 中重寫父類的 p2 方法時,可以是public 或者是 protected,但不能是 <default> 和 private
[ 返回頂部 ]
四、初始化順序
1)初始化順序:父類靜態屬性 -> 子類靜態屬性 -> 父類非靜態屬性 -> 父類構造器調用 -> 子類非靜態屬性 -> 子類構造器調用
package com.example; public class T{ public T(int i){ System.out.println("T" + "(" + i + ")"); } }
package com.example; public class Person{ // 父類 public static T t1 = new T(1); // 靜態屬性 public T t2 = new T(2); // 非靜態屬性 public Person(){ System.out.println("Person()"); } public static T t3 = new T(3); // 靜態屬性 }
package com.example; public class Man extends Person{ // 子類 public static T t4 = new T(4); // 靜態屬性 public T t5 = new T(5); // 非靜態屬性 public Man(){ System.out.println("Man()"); } public static T t6 = new T(6); // 靜態屬性 }
package com.example; public class Test{ public static void main(String[] args){ new Man(); } }
運行結果: T(1) T(3) T(4) T(6) T(2) Person() T(5) Man()
[ 返回頂部 ]
五、@Override 與方法重寫
1)Java 為了保證方法重寫時不會出現誤寫成方法重載的情況,所以引入了 @Override
package com.example; public class Person{ // 父類 public void speak(int i){ System.out.println("Person: speak" + "(" + i + ")"); } }
package com.example; public class Man extends Person{ // 子類 /* @Override public void speak(String str){ System.out.println("Man: speak" + "(" + str + ")"); } */ // error,用了 @Override ,那麼 @Override 下麵的方法必須是方法重寫,否則編譯時會出錯 @Override public void speak(int i){ System.out.println("Man: speak" + "(" + i + ")"); } }
package com.example; public class Test{ public static void main(String[] args){ Man m = new Man(); m.speak(10); } }
運行結果: Man: speak(10)
[ 返回頂部 ]
六、繼承抽象類
1)繼承抽象類時,如果父類中有抽象方法,那麼子類必須全部重寫父類中的抽象方法
package com.example; public abstract class Person{ public abstract void p1(); // 抽象方法 public abstract void p2(); public void p3(){ System.out.println("Person: p3()"); } public void p4(){ System.out.println("Person: p4()"); } }
package com.example; public class Man extends Person{ @Override public void p1(){ } @Override public void p2(){ } }
[ 返回頂部 ]
七、final 關鍵字
1)由 final 關鍵字修飾的類不能被繼承,例如:public final class Person{}
2)在繼承關係中,如果父類的方法由 final 關鍵字修飾,那麼該方法將不能被子類重寫
[ 返回頂部 ]
參考資料:
《Java 編程思想》第4版