動手動腦---繼承與多態
一.為什麽子類的構造方法在運行之前,必須調用父類的構造方法?能不能反過來?為什麽不能反過來?
class Grandparent { public Grandparent() { System.out.println("GrandParent Created."); } public Grandparent(String string) { System.out.println("GrandParent Created.String:" + string); } } class Parent extends Grandparent {public Parent() { super("Hello.Grandparent."); System.out.println("Parent Created"); // super("Hello.Grandparent."); } } class Child extends Parent { public Child() { System.out.println("Child Created"); } } public class TestInherits {public static void main(String args[]) { Child c = new Child(); } }
構造函數(constructor)是一種特殊的方法 。主要用來在創建對象時初始化對象, 即為對象成員變量賦初始值,總與new運算符一起使用在創建對象的語句中 。
特別的一個類可以有多個構造函數 ,可根據其參數個數的不同或參數類型的不同來區分它們 即構造函數的重載。構造函數的功能主要用於在類的對象創建時定義初始化的狀態。
因為子類繼承自父類,會沿用父類的東西(沒被覆蓋的函數以及可見的成員變量等),而這些東西子類是沒有的,需要先初始化父類才能被使用。
構造一個對象,先調用其構造方法,來初始化其成員函數和成員變量。
子類擁有父的成員變量和成員方法,如果不調用,則從父類繼承而來的成員變量和成員方法得不到正確的初始化。
不能反過來調用也是這個原因,因為父類根本不知道子類有神魔變量而且這樣一來子類也得不到初始化的父類變量,導致程序運行出錯!
二.
public class ExplorationJDKSource { /** * @param args */ public static void main(String[] args) { System.out.println(new A()); } } class A{}
反編譯結果:
使用Eclipse打開JDK源碼:
打開object
這與課件給出基本相同
三.
class Animal { void eat() { System.out.println("animal : eat"); } } class Dog extends Animal { void eat() { System.out.println("dog : eat"); } void eatTest() { this.eat(); // this 調用自己的方法 super.eat(); // super 調用父類方法 } } public class Test { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Dog d = new Dog(); d.eatTest(); } }
結果:
總結:
(1)覆蓋方法的允許訪問範圍不能小於原方法。
(2)覆蓋方法所拋出的異常不能比原方法更多。
(3)聲明為final方法不允許覆蓋。 例如,Object的getClass()方法不能覆蓋。
(4)不能覆蓋靜態方法。
四.下列語句哪一個將引起編譯錯誤?為什麽?哪一個會引起運行時錯誤?為什麽?
class Mammal{} class Dog extends Mammal {} class Cat extends Mammal{} public class TestCast { public static void main(String args[]) { Mammal m; Dog d=new Dog(); Cat c=new Cat(); m=d; //d=m; d=(Dog)m; // d=c; // c=(Cat)m; } }
加//的都為出錯項,子類可賦值給父類,父類不能賦值給子類,需進行類型轉換
總結:
子類對象可以直接賦給基類變量。
基類對象要賦給子類對象變量,必須執行類型轉換,
其語法是: 子類對象變量=(子類名)基類對象名;
也不能亂轉換。如果類型轉換失敗Java會拋出以下這種異常: ClassCastException
五.1. 左邊的程序運行結果是什麽? 2. 你如何解釋會得到這樣的輸出? 3. 計算機是不會出錯的,之所以得 到這樣的運行結果也是有原因的, 那麽從這些運行結果中,你能總 結出Java的哪些語法特性?
public class ParentChildTest { public static void main(String[] args) { Parent parent=new Parent(); parent.printValue(); Child child=new Child(); child.printValue(); parent=child; parent.printValue(); parent.myValue++; parent.printValue(); ((Child)parent).myValue++; parent.printValue(); } } class Parent{ public int myValue=100; public void printValue() { System.out.println("Parent.printValue(),myValue="+myValue); } } class Child extends Parent{ public int myValue=200; public void printValue() { System.out.println("Child.printValue(),myValue=*****************"+myValue); } }
預測:100 200 200 201 202
結果:
第一個值:父類Parent調用自己的方法;
第二個值:子類child調用方自己的方法;
第三個值:子類將自己的方法賦給父類,父類雖然名字不變,但已經是子類了,因而實際調用的是子類的方法;
第四個值:父類中的值MyValue加一,但是父類調用的是子類的方法,因而無影響;
第五個值:強制轉化,父類將值賦給子類,調用的仍然是子類的方法,只是值加一
總結:
當子類與父類擁有一樣的方法,並且讓一個父類變量引用一個子類對象時,到底調用哪個方法,由對象自己的“真實”類型所決定,這就是說:對象是子類型的,它就調用子類型的方法,是父類型的,它就調用父類型的方法。 這個特性實際上就是面向對象“多態”特性的具體表現。
如果子類與父類有相同的字段,則子類中的字段會代替或隱藏父類的字段,子類方法中訪問的是子類中的字段(而不是父類中的字段)。如果子類方法確實想訪問父類中被隱藏的同名字段,可以用super關鍵字來訪問它。 如果子類被當作父類使用,則通過子類訪問的字段是父類的! 牢記:在實際開發中,要避免在子類中定義與父類同名 的字段。不要自找麻煩!——但考試除外,考試 中出這種題還是可以的。
六.請使用javap查看編譯器為TestPolymorphism.java生成的字節碼指令,然後通過互聯網搜索資料,嘗試從底層開始理解Java編譯器是如何為多態代碼生成字節碼指令,在程序運行過程中,多態特性又是如何實現的。
class Parent { public int value=100; public void Introduce() { System.out.println("I‘m father"); } } class Son extends Parent { public int value=101; public void Introduce() { System.out.println("I‘m son"); } } class Daughter extends Parent { public int value=102; public void Introduce() { System.out.println("I‘m daughter"); } } public class TestPolymorphism { public static void main(String args[]) { Parent p=new Parent(); p.Introduce(); System.out.println(p.value); p=new Son(); p.Introduce(); System.out.println(p.value); p=new Daughter(); p.Introduce(); System.out.println(p.value); } }
結果:
總結:
◆強制的:一種隱 式做類型轉換的方法。
◆重載的:將一個標誌符用作多個意義。
◆參數的:為不同類型的參數提供相同的操作。
◆包含的:類包含關系的抽象操作。
多態依賴於類型和實現的分離,多用來把接口和實現分離。
動手動腦---繼承與多態