繼承與多型之動手動腦
繼承與多型
一、繼承條件下的構造方法的呼叫
class Grandparent { public Grandparent() { System.out.println("GrandParent Created."); } public Grandparent(String string) { System.out.println("GrandParent Created.String:" + string); } } class Parentt extends Grandparent { public Parentt() { super("Hello.Grandparent."); System.out.println("Parent Created"); // super("Hello.Grandparent."); } public Parentt(String s) { System.out.println("Parent Created.String:"+s); } } class Childd extends Parentt { public Childd() { super("Hello.Parent."); System.out.println("Child Created"); } } public class TestInherits { public static void main(String args[]) { Childd c = new Childd(); } }
輸出:
GrandParent Created.
Parent Created.String:Hello.Parent.
Child Created
在構造子類的方法之前會先構造父類的方法,如果是多級繼承,會先執行最頂級父類的構造方法,然後依次執行各級個子類的構造方法。若想在子類中呼叫父類的有參構造方法,需要在子類的構造方法的第一行加上super方法,注意:只能是第一行
為什麼子類的構造方法在執行之前,必須呼叫父類的構造方法?能不能反過來?為什麼不能反過來?
因為子類繼承自父類,會沿用父類的東西(沒被覆蓋的函式以及可見的成員變數等),而這些東西子類是沒有的,需要先初始化父類才能被使用。構造一個物件,先呼叫其構造方法,來初始化其成員函式和成員變數。
子類擁有父的成員變數和成員方法,如果不呼叫,則從父類繼承而來的成員變數和成員方法得不到正確的初始化。
不能反過來呼叫也是這個原因,因為父類根本不知道子類有神魔變數而且這樣一來子類也得不到初始化的父類變數,導致程式執行出錯!
二、不允許繼承的類
不可變的“類”有何作用:
①可方便和安全的用於多執行緒的環境中
②訪問他們不用加鎖,可高效能的執行
public final class Address { private final String detail; private final String postCode; //在構造方法裡初始化兩個例項屬性 public Address() { this.detail = ""; this.postCode = ""; } public Address(String detail , String postCode) { this.detail = detail; this.postCode = postCode; } //僅為兩個例項屬性提供getter方法 public String getDetail() { return this.detail; } public String getPostCode() { return this.postCode; } //重寫equals方法,判斷兩個物件是否相等。 public boolean equals(Object obj) { if (obj instanceof Address) { Address ad = (Address)obj; if (this.getDetail().equals(ad.getDetail()) && this.getPostCode().equals(ad.getPostCode())) { return true; } } return false; } public int hashCode() { return detail.hashCode() + postCode.hashCode(); } public static void main(String args[]) { Address add=new Address("123","456"); System.out.println(add.getDetail()); System.out.println(add.getPostCode()); Address arr=new Address("123","456"); System.out.println(add.equals(arr)); } }
輸出:
123
456
true
三、反彙編
public class ExplorationJDKSource { /** * @param args */ public static void main(String[] args) { System.out.println(new A()); } } class A{}
輸出:
A@15db9742
我們用javap -c 來反編譯該檔案的.class檔案,得到:
點進object裡,發現tostring方法
public void main(object x)呼叫了String類的valueOf方法,
故而出現了這樣的輸出
三、神奇的“+”號和方法覆蓋
public class Fruit { public String toString() { return "Fruit toString."; } public static void main(String args[]) { Fruit f=new Fruit(); System.out.println("f="+f); System.out.println("f="+f.toString()); } }
在輸出字串+物件時,會隱式的呼叫tostring()的方法
方法的覆蓋:要求子類和父類的方法一樣,並且實現在子類中呼叫父類的被覆蓋的方法
class ABC { public void print() { System.out.println("Parent print"); } } public class test extends ABC{ public void print() { super.print(); System.out.println("Children print"); } public static void main(String[] args) { // TODO Auto-generated method stub test t1=new test(); t1.print(); } }
輸出:
Parent print
Children print
利用super()的方法呼叫父類的物件,然後再呼叫父類的覆蓋方法
覆蓋的語法規則:
四、如何判斷物件是否可以轉換
public class TestInstanceof { public static void main(String[] args) { //宣告hello時使用Object類,則hello的編譯型別是Object,Object是所有類的父類 //但hello變數的實際型別是String Object hello = "Hello"; //String是Object類的子類,所以返回true。 System.out.println("字串是否是Object類的例項:" + (hello instanceof Object)); //返回true。 System.out.println("字串是否是String類的例項:" + (hello instanceof String)); //返回false。 System.out.println("字串是否是Math類的例項:" + (hello instanceof Math)); //String實現了Comparable介面,所以返回true。 System.out.println("字串是否是Comparable介面的例項:" + (hello instanceof Comparable)); String a = "Hello"; //String類既不是Math類,也不是Math類的父類,所以下面程式碼編譯無法通過 //System.out.println("字串是否是Math類的例項:" + (a instanceof Math)); } }
輸出:
字串是否是Object類的例項:true
字串是否是String類的例項:true
字串是否是Math類的例項:false
字串是否是Comparable介面的例項:true
型別轉換事例:
import java.net.InterfaceAddress; 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; System.out.println("good"); } }
將子類dag賦值給基類m,可以實現,而將基類賦值給d需要進行強制型別轉換,兩個子類之間不能進行轉換,m已經是Dog類,不能再被強制轉換為Cat類
五、變態的類
public class ParentChildTest { public static void main(String[] args) { Parent parent=new Parent(); parent.printValue();//100 Child child=new Child(); child.printValue();//200 //父類變數去引用子類物件 parent=child; parent.printValue();//呼叫子類的方法200 //如果子類被當作父類使用,則通過子類訪問的欄位是父類的! parent.myValue++; System.out.println(parent.myValue);//101 parent.printValue();//200 ((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); } }
輸出:
Parent.printValue(),myValue=100
Child.printValue(),myValue=200
Child.printValue(),myValue=200
101
Child.printValue(),myValue=200
Child.printValue(),myValue=201
前兩個輸出沒問題,將子類賦值給基類物件,基類物件指向子類物件,呼叫的是子類的方法,故而輸出第三條,
parent.myValue++;就是將子類當成父類來使用,通過子類訪問的欄位是父類的所以,當輸出myvalue時是101,是父類的值+1;
最後將基類強制轉換成子類後,將value++,此時訪問的才是子類的欄位,故而輸出結果為201.
六、多型
package zoo4; import java.util.Vector; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); Vector<Animal> ans = new Vector<Animal>(); //飼養員小李餵養一隻獅子 ans.add(new Lion()); //飼養員小李餵養十隻猴子 for (int i = 0; i < 10; i++) { ans.add(new Monkey()); } //飼養員小李餵養5只鴿子 for (int i = 0; i < 5; i++) { ans.add(new Pigeon()); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Vector<Animal> ans) { for (Animal an : ans) { an.eat(); } } } abstract class Animal { public abstract void eat(); } class Lion extends Animal { public void eat() { System.out.println("我不吃肉誰敢吃肉!"); } } class Monkey extends Animal { public void eat() { System.out.println("我什麼都吃,尤其喜歡香蕉。"); } } class Pigeon extends Animal { public void eat() { System.out.println("我要減肥,所以每天只吃一點大米。"); } }
輸出:
我不吃肉誰敢吃肉!
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我什麼都吃,尤其喜歡香蕉。
我要減肥,所以每天只吃一點大米。
我要減肥,所以每天只吃一點大米。
我要減肥,所以每天只吃一點大米。
我要減肥,所以每天只吃一點大米。
我要減肥,所以每天只吃一點大米。
通過定義一個抽象類anmial,來控制所以動物吃的功能,通過vector這個可變長陣列,來控制動物的增減,最終實現當再加入其他不同的動物時,用較少的修改來完成動物的新增。
除了可以用抽象類來實現,還可以使用介面的形式來實現
package zoo5; import java.util.Vector; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); Vector<Animal> ans = new Vector<Animal>(); //飼養員小李餵養一隻獅子 ans.add(new Lion()); //飼養員小李餵養十隻猴子 for (int i = 0; i < 10; i++) { ans.add(new Monkey()); } //飼養員小李餵養5只鴿子 for (int i = 0; i < 5; i++) { ans.add(new Pigeon()); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Vector<Animal> ans) { for (Animal an : ans) { an.eat(); } } } interface Animal { public void eat(); } class Lion implements Animal { public void eat() { System.out.println("我不吃肉誰敢吃肉!"); } } class Monkey implements Animal { public void eat() { System.out.println("我什麼都吃,尤其喜歡香蕉。"); } } class Pigeon implements Animal { public void eat() { System.out.println("我要減肥,所以每天只吃一點大米。"); } }
抽象類與抽象方法
抽象類的三種方法:
抽象類不能用來建立物件,一般用他來引用子類的物件
介面:
介面的使用:
介面的擴充:
介面與抽象類的區別:
&n