Java超詳細(xì)講解繼承和多態(tài)的使用
繼承和多態(tài)
學(xué)習(xí)繼承、組合、多態(tài)
1、繼承
1.1、繼承概念
專門用來(lái)進(jìn)行共性抽取,實(shí)現(xiàn)代碼復(fù)用。 它允許程序員在保持原有類特 性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加新功能,這樣產(chǎn)生新的類,稱派生類。
1.2、繼承的語(yǔ)法
在Java中如果要表示類之間的繼承關(guān)系,需要借助extends關(guān)鍵字
class Animal{ public String name; public int age; public String sex; public void eat(){ System.out.println(this.name+"cat::eat()!"); } public void sleep(){ System.out.println(this.name+"睡覺(jué)!"); } } /** * 繼承 其實(shí)就是對(duì)共性的抽取 從而達(dá)到了代碼的復(fù)用 */ class Cat extends Animal{ public void mew(){ System.out.println(this.name+"cat::mew()!"); } } class Dog extends Animal{ public void bark(){ System.out.println("dog::bark()!"); } } public class TestDemo { public static void main(String[] args) { Cat cat = new Cat(); cat.name = "咪咪"; cat.sleep(); cat.eat(); cat.mew(); } }
注意:
1. 子類會(huì)將父類中的成員變量或者成員方法繼承到子類中了
2. 子類繼承父類之后,盡量新添加自己特有的成員,體現(xiàn)出與基類的不同,否則就沒(méi)有必要繼承了
1.3、父類成員的訪問(wèn)
1.31、子類和父類不存在同名成員變量
public class Base { int a; int b; } public class Derived extends Base{ int c; public void method(){ a = 10; // 訪問(wèn)從父類中繼承下來(lái)的a b = 20; // 訪問(wèn)從父類中繼承下來(lái)的b c = 30; // 訪問(wèn)子類自己的c } }
1.32、子類和父類成員變量同名
class Base { public int a = 1; public int b = 2; public void method(){ System.out.println("BASE::TEST()"); } } class Derived extends Base{ public int a = 3; public int d = 4; public void method2(){ System.out.println("Derived::method2()"); } public void test(){ method2(); method(); /*System.out.println(a);//如果重名類 優(yōu)先訪問(wèn)自己的 System.out.println(super.a);//寫代碼的時(shí)候 讓這個(gè)代碼更已讀 System.out.println(this.b); System.out.println(d);*/ } } public class TestDemo2 { public static void main(String[] args) { Derived derived = new Derived(); derived.test(); } }
1、如果訪問(wèn)的成員變量子類中有,訪問(wèn)自己的成員變量。
2、如果訪問(wèn)的成員變量子類中無(wú),則訪問(wèn)父類繼承下來(lái)的,如果父類也沒(méi)有定義,則編譯報(bào)錯(cuò)。
3、如果訪問(wèn)的成員變量與父類中成員變量同名,則優(yōu)先訪問(wèn)自己的,即:子類將父類同名成員隱藏了。
4、成員變量訪問(wèn)遵循就近原則,自己有優(yōu)先自己的,如果沒(méi)有則向父類中找。
比如:你和你父親各自有一款相同的手機(jī),平時(shí)使用時(shí)你肯定優(yōu)先用自己的,如果自己手機(jī)沒(méi)電了,你才會(huì)考慮使用父親的。
1.33、成員方法名字不同
public class Base { public void methodA(){ System.out.println("Base中的methodA()"); } } public class Derived extends Base{ public void methodB(){ System.out.println("Derived中的methodB()方法"); } public void methodC(){ methodB(); // 訪問(wèn)子類自己的methodB() methodA(); // 訪問(wèn)父類繼承的methodA() // methodD(); // 編譯失敗,在整個(gè)繼承體系中沒(méi)有發(fā)現(xiàn)方法methodD() } }
總結(jié):成員方法沒(méi)有同名時(shí),在子類方法中或者通過(guò)子類對(duì)象訪問(wèn)方法時(shí),則優(yōu)先訪問(wèn)自己的,自己沒(méi)有時(shí) 再到父類中找,如果父類中也沒(méi)有則報(bào)錯(cuò)。
1.34、 成員方法名字相同
public class Base { public void methodA(){ System.out.println("Base中的methodA()"); } public void methodB(){ System.out.println("Base中的methodB()"); } } public class Derived extends Base{ public void methodA(int a) { System.out.println("Derived中的method(int)方法"); } public void methodB(){ System.out.println("Derived中的methodB()方法"); } public void methodC(){ methodA(); // 沒(méi)有傳參,訪問(wèn)父類中的methodA() methodA(20); // 傳遞int參數(shù),訪問(wèn)子類中的methodA(int) methodB(); // 直接訪問(wèn),則永遠(yuǎn)訪問(wèn)到的都是子類中的methodB(),基類的無(wú)法訪問(wèn)到 } }
通過(guò)子類對(duì)象訪問(wèn)父類與子類中不同名方法時(shí),優(yōu)先在子類中找,找到則訪問(wèn),否則在父類中找,找到 則訪問(wèn),否則編譯報(bào)錯(cuò)。 通過(guò)派生類對(duì)象訪問(wèn)父類與子類同名方法時(shí),如果父類和子類同名方法的參數(shù)列表不同(重載),根據(jù)調(diào)用 方法適傳遞的參數(shù)選擇合適的方法訪問(wèn),如果沒(méi)有則報(bào)錯(cuò);如果父類和子類同名方法的原型一致,則只能訪問(wèn)到子類的,父類的無(wú)法通過(guò)派生類對(duì)象直接訪問(wèn)到。 問(wèn)題:如果子類中存在與父類中相同的成員時(shí),那如何在子類中訪問(wèn)父類相同名稱的成員呢?
1.4、super關(guān)鍵字
public class Base { int a; int b; public void methodA(){ System.out.println("Base中的methodA()"); } public void methodB(){ System.out.println("Base中的methodB()"); } } public class Derived extends Base{ int a; // 與父類中成員變量同名且類型相同 char b; // 與父類中成員變量同名但類型不同 // 與父類中methodA()構(gòu)成重載 public void methodA(int a) { System.out.println("Derived中的method()方法"); } // 與基類中methodB()構(gòu)成重寫 public void methodB(){ System.out.println("Derived中的methodB()方法"); } public void methodC(){ // 對(duì)于同名的成員變量,直接訪問(wèn)時(shí),訪問(wèn)的都是子類的 a = 100; // 等價(jià)于: this.a = 100; b = 101; // 等價(jià)于: this.b = 101; // 注意:this是當(dāng)前對(duì)象的引用 // 訪問(wèn)父類的成員變量時(shí),需要借助super關(guān)鍵字 // super是獲取到子類對(duì)象中從基類繼承下來(lái)的部分 super.a = 200; super.b = 201; // 父類和子類中構(gòu)成重載的方法,直接可以通過(guò)參數(shù)列表區(qū)分清訪問(wèn)父類還是子類方法 methodA(); // 沒(méi)有傳參,訪問(wèn)父類中的methodA() methodA(20); // 傳遞int參數(shù),訪問(wèn)子類中的methodA(int) // 如果在子類中要訪問(wèn)重寫的基類方法,則需要借助super關(guān)鍵字 methodB(); // 直接訪問(wèn),則永遠(yuǎn)訪問(wèn)到的都是子類中的methodB(),基類的無(wú)法訪問(wèn)到 super.methodB(); // 訪問(wèn)基類的methodB() } }
【注意事項(xiàng)】
1. 只能在非靜態(tài)方法中使用
2. 在子類方法中,訪問(wèn)父類的成員變量和方法。
1.5、子類構(gòu)造方法
父子父子,先有父再有子,即:子類對(duì)象構(gòu)造時(shí),需要先調(diào)用基類構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。
public class Base { public Base(){ System.out.println("Base()"); } } public class Derived extends Base{ public Derived(){ super(); // 注意子類構(gòu)造方法中默認(rèn)會(huì)調(diào)用基類的無(wú)參構(gòu)造方法:super(), // 用戶沒(méi)有寫時(shí),編譯器會(huì)自動(dòng)添加,而且super()必須是子類構(gòu)造方法中第一條語(yǔ)句, // 并且只能出現(xiàn)一次 System.out.println("Derived()"); } } public class Test { public static void main(String[] args) { Derived d = new Derived(); } }
結(jié)果打?。?br />Base()
Derived()
在子類構(gòu)造方法中,并沒(méi)有寫任何關(guān)于基類構(gòu)造的代碼,但是在構(gòu)造子類對(duì)象時(shí),先執(zhí)行基類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法,因?yàn)椋鹤宇悓?duì)象中成員是有兩部分組成的,基類繼承下來(lái)的以及子類新增加的部分 。父子父子 肯定是先有父再有子,所以在構(gòu)造子類對(duì)象時(shí)候 ,先要調(diào)用基類的構(gòu)造方法,將從基類繼承下來(lái)的成員構(gòu)造完整,然后再調(diào)用子類自己的構(gòu)造方法,將子類自己新增加的成員初始化完整 。
注意:
1. 若父類顯式定義無(wú)參或者默認(rèn)的構(gòu)造方法,在子類構(gòu)造方法第一行默認(rèn)有隱含的super()調(diào)用,即調(diào)用基類構(gòu)
造方法
2. 如果父類構(gòu)造方法是帶有參數(shù)的,此時(shí)編譯器不會(huì)再給子類生成默認(rèn)的構(gòu)造方法,此時(shí)需要用戶為子類顯式
定義構(gòu)造方法,并在子類構(gòu)造方法中選擇合適的父類構(gòu)造方法調(diào)用,否則編譯失敗。
3. 在子類構(gòu)造方法中,super(…)調(diào)用父類構(gòu)造時(shí),必須是子類構(gòu)造函數(shù)中第一條語(yǔ)句。
4. super(…)只能在子類構(gòu)造方法中出現(xiàn)一次,并且不能和this同時(shí)出現(xiàn)
1.6、super和this
【相同點(diǎn)】
1. 都是Java中的關(guān)鍵字
2. 只能在類的非靜態(tài)方法中使用,用來(lái)訪問(wèn)非靜態(tài)成員方法和字段
3. 在構(gòu)造方法中調(diào)用時(shí),必須是構(gòu)造方法中的第一條語(yǔ)句,并且不能同時(shí)存在
【不同點(diǎn)】
1. this是當(dāng)前對(duì)象的引用,當(dāng)前對(duì)象即調(diào)用實(shí)例方法的對(duì)象,super相當(dāng)于是子類對(duì)象中從父類繼承下來(lái)部分成員的引用
2. 在非靜態(tài)成員方法中,this用來(lái)訪問(wèn)本類的方法和屬性,super用來(lái)訪問(wèn)父類繼承下來(lái)的方法和屬性
3. this是非靜態(tài)成員方法的一個(gè)隱藏參數(shù),super不是隱藏的參數(shù)
4. 成員方法中直接訪問(wèn)本類成員時(shí),編譯之后會(huì)將this還原,即本類非靜態(tài)成員都是通過(guò)this來(lái)訪問(wèn)的;在子類中如果通過(guò)super訪問(wèn)父類成員,編譯之后在字節(jié)碼層面super實(shí)際是不存在的
5. 在構(gòu)造方法中:this(…)用于調(diào)用本類構(gòu)造方法,super(…)用于調(diào)用父類構(gòu)造方法,兩種調(diào)用不能同時(shí)在構(gòu)造方法中出現(xiàn)
6. 構(gòu)造方法中一定會(huì)存在super(…)的調(diào)用,用戶沒(méi)有寫編譯器也會(huì)增加,但是this(…)用戶不寫則沒(méi)有
1.7、初始化
class Person { public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; System.out.println("構(gòu)造方法執(zhí)行"); } { System.out.println("實(shí)例代碼塊執(zhí)行"); } static { System.out.println("靜態(tài)代碼塊執(zhí)行"); } } public class TestDemo { public static void main(String[] args) { Person person1 = new Person("bit",10); System.out.println("============================"); Person person2 = new Person("gaobo",20); } }
執(zhí)行結(jié)果:
靜態(tài)代碼塊執(zhí)行
實(shí)例代碼塊執(zhí)行
構(gòu)造方法執(zhí)行
============================
實(shí)例代碼塊執(zhí)行
構(gòu)造方法執(zhí)行
注意:
1. 靜態(tài)代碼塊先執(zhí)行,并且只執(zhí)行一次,在類加載階段執(zhí)行
2. 當(dāng)有對(duì)象創(chuàng)建時(shí),才會(huì)執(zhí)行實(shí)例代碼塊,實(shí)例代碼塊執(zhí)行完成后,最后構(gòu)造方法執(zhí)行
繼承關(guān)系上的執(zhí)行順序:
1、父類靜態(tài)代碼塊優(yōu)先于子類靜態(tài)代碼塊執(zhí)行,且是最早執(zhí)行
2、父類實(shí)例代碼塊和父類構(gòu)造方法緊接著執(zhí)行
3、子類的實(shí)例代碼塊和子類構(gòu)造方法緊接著再執(zhí)行
4、第二次實(shí)例化子類對(duì)象時(shí),父類和子類的靜態(tài)代碼塊都將不會(huì)再執(zhí)行
1.8、protected關(guān)鍵字
// 為了掩飾基類中不同訪問(wèn)權(quán)限在子類中的可見(jiàn)性,為了簡(jiǎn)單類B中就不設(shè)置成員方法了 // extend01包中 public class B { private int a; protected int b; public int c; int d; } // extend01包中 // 同一個(gè)包中的子類 public class D extends B{ public void method(){ // super.a = 10; // 編譯報(bào)錯(cuò),父類private成員在相同包子類中不可見(jiàn) super.b = 20; // 父類中protected成員在相同包子類中可以直接訪問(wèn) super.c = 30; // 父類中public成員在相同包子類中可以直接訪問(wèn) super.d = 40; // 父類中默認(rèn)訪問(wèn)權(quán)限修飾的成員在相同包子類中可以直接訪問(wèn) } } // extend02包中 // 不同包中的子類 public class C extends B { public void method(){ // super.a = 10; // 編譯報(bào)錯(cuò),父類中private成員在不同包子類中不可見(jiàn) super.b = 20; // 父類中protected修飾的成員在不同包子類中可以直接訪問(wèn) super.c = 30; // 父類中public修飾的成員在不同包子類中可以直接訪問(wèn) //super.d = 40; // 父類中默認(rèn)訪問(wèn)權(quán)限修飾的成員在不同包子類中不能直接訪問(wèn) } } // extend02包中 // 不同包中的類 public class TestC { public static void main(String[] args) { C c = new C(); c.method(); // System.out.println(c.a); // 編譯報(bào)錯(cuò),父類中private成員在不同包其他類中不可見(jiàn) // System.out.println(c.b); // 父類中protected成員在不同包其他類中不能直接訪問(wèn) System.out.println(c.c); // 父類中public成員在不同包其他類中可以直接訪問(wèn) // System.out.println(c.d); // 父類中默認(rèn)訪問(wèn)權(quán)限修飾的成員在不同包其他類中不能直接訪問(wèn) } }
注意:父類中private成員變量隨時(shí)在子類中不能直接訪問(wèn),但是也繼承到子類中了
1.9、繼承方式
1.10、final關(guān)鍵字
final關(guān)鍵可以用來(lái)修飾變量、成員方法以及類。
1. 修飾變量或字段,表示常量(即不能修改)
2. 修飾類:表示此類不能被繼承
2、繼承與組合
組合表示對(duì)象之間的關(guān)系
// 輪胎類 class Tire{ // ... } // 發(fā)動(dòng)機(jī)類 class Engine{ // ... } // 車載系統(tǒng)類 class VehicleSystem{ // ... } class Car{ private Tire tire; // 可以復(fù)用輪胎中的屬性和方法 private Engine engine; // 可以復(fù)用發(fā)動(dòng)機(jī)中的屬性和方法 private VehicleSystem vs; // 可以復(fù)用車載系統(tǒng)中的屬性和方法 // ... } // 奔馳是汽車 class Benz extend Car{ // 將汽車中包含的:輪胎、發(fā)送機(jī)、車載系統(tǒng)全部繼承下來(lái) }
3、多態(tài)
3.1、多態(tài)概念
通俗來(lái)說(shuō),就是多種形態(tài),具體點(diǎn)就是去完成某個(gè)行為,當(dāng)不同的對(duì)象去完成時(shí)會(huì)產(chǎn)生出不同 的狀態(tài)。
3.2、多態(tài)實(shí)現(xiàn)條件
1. 必須在繼承體系下
2. 子類必須要對(duì)父類中方法進(jìn)行重寫
3. 通過(guò)父類的引用調(diào)用重寫的方法
public class Animal { String name; int age; public Animal(String name, int age){ this.name = name; this.age = age; } public void eat(){ System.out.println(name + "吃飯"); } } public class Cat extends Animal{ public Cat(String name, int age){ super(name, age); } @Override public void eat(){ System.out.println(name+"吃魚~~~"); } } public class Dog extends Animal { public Dog(String name, int age){ super(name, age); } @Override public void eat(){ System.out.println(name+"吃骨頭~~~"); } } ///分割線// public class TestAnimal { // 編譯器在編譯代碼時(shí),并不知道要調(diào)用Dog 還是 Cat 中eat的方法 // 等程序運(yùn)行起來(lái)后,形參a引用的具體對(duì)象確定后,才知道調(diào)用那個(gè)方法 // 注意:此處的形參類型必須時(shí)父類類型才可以 public static void eat(Animal a){ a.eat(); } public static void main(String[] args) { Cat cat = new Cat("元寶",2); Dog dog = new Dog("小七", 1); eat(cat); eat(dog); } }
運(yùn)行結(jié)果:
元寶吃魚~~~
元寶正在睡覺(jué)
小七吃骨頭~~~
小七正在睡覺(jué)
3.3、重寫
重寫(override):也稱為覆蓋。重寫是子類對(duì)父類非靜態(tài)、非private修飾,非final修飾,非構(gòu)造方法等的實(shí)現(xiàn)過(guò)程進(jìn)行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫!重寫的好處在于子類可以根據(jù)需要,定義特定于自己的行為。 也就是說(shuō)子類能夠根據(jù)需要實(shí)現(xiàn)父類的方法。
1. 子類在重寫父類的方法時(shí),一般必須與父類方法原型一致:修飾符 返回值類型 方法名(參數(shù)列表) 要完全一致
2. JDK7以后,被重寫的方法返回值類型可以不同,但是必須是具有父子關(guān)系的
訪問(wèn)權(quán)限不能比父類中被重寫的方法的訪問(wèn)權(quán)限更低。例如:如果父類方法被public修飾,則子類中重寫該方法就不能聲明為 protected
3. 父類被static、private修飾的方法、構(gòu)造方法都不能被重寫。
子類和父類在同一個(gè)包中,那么子類可以重寫父類所有方法,除了聲明為 private 和 final 的方法。
4. 子類和父類不在同一個(gè)包中,那么子類只能夠重寫父類的聲明為 public 和 protected 的非 final 方法。
5. 重寫的方法, 可以使用 @Override 注解來(lái)顯式指定. 有了這個(gè)注解能幫我們進(jìn)行一些合法性校驗(yàn). 例如不小心將方法名字拼寫錯(cuò)了 (比如寫成 aet), 那么此時(shí)編譯器就會(huì)發(fā)現(xiàn)父類中沒(méi)有 aet 方法, 就會(huì)編譯報(bào)錯(cuò), 提示無(wú)法構(gòu)成重寫.
重載與重寫區(qū)別:
方法重載是一個(gè)類的多態(tài)性表現(xiàn),而方法重寫是子類與父類的一種多態(tài)性表現(xiàn)。
3.4、向上轉(zhuǎn)型和向下轉(zhuǎn)型
向上轉(zhuǎn)型:實(shí)際就是創(chuàng)建一個(gè)子類對(duì)象,將其當(dāng)成父類對(duì)象來(lái)使用。
語(yǔ)法格式:父類類型 對(duì)象名 = new 子類類型()
【使用場(chǎng)景】
1. 直接賦值
2. 方法傳參
3. 方法返回
public class TestAnimal { // 2. 方法傳參:形參為父類型引用,可以接收任意子類的對(duì)象 public static void eatFood(Animal a){ a.eat(); } // 3. 作返回值:返回任意子類對(duì)象 public static Animal buyAnimal(String var){ if("狗" == var){ return new Dog("狗狗",1); }else if("貓" == var){ return new Cat("貓貓", 1); }else{ return null; } } public static void main(String[] args) { Animal cat = new Cat("元寶",2); // 1. 直接賦值:子類對(duì)象賦值給父類對(duì)象 Dog dog = new Dog("小七", 1); eatFood(cat); eatFood(dog); Animal animal = buyAnimal("狗"); animal.eat(); animal = buyAnimal("貓"); animal.eat(); } }
將一個(gè)子類對(duì)象經(jīng)過(guò)向上轉(zhuǎn)型之后當(dāng)成父類方法使用,再無(wú)法調(diào)用子類的方法,但有時(shí)候可能需要調(diào)用子類特有的
方法,此時(shí):將父類引用再還原為子類對(duì)象即可,即向下轉(zhuǎn)換。
public class TestAnimal { public static void main(String[] args) { Cat cat = new Cat("元寶",2); Dog dog = new Dog("小七", 1); // 向上轉(zhuǎn)型 Animal animal = cat; animal.eat(); animal = dog; animal.eat(); // 編譯失敗,編譯時(shí)編譯器將animal當(dāng)成Animal對(duì)象處理 // 而Animal類中沒(méi)有bark方法,因此編譯失敗 // animal.bark(); // 向上轉(zhuǎn)型 // 程序可以通過(guò)編程,但運(yùn)行時(shí)拋出異常---因?yàn)椋篴nimal實(shí)際指向的是狗 // 現(xiàn)在要強(qiáng)制還原為貓,無(wú)法正常還原,運(yùn)行時(shí)拋出:ClassCastException cat = (Cat)animal; cat.mew(); // animal本來(lái)指向的就是狗,因此將animal還原為狗也是安全的 dog = (Dog)animal; dog.bark(); } }
向下轉(zhuǎn)型用的比較少,而且不安全,萬(wàn)一轉(zhuǎn)換失敗,運(yùn)行時(shí)就會(huì)拋異常。Java中為了提高向下轉(zhuǎn)型的安全性,引入
了 instanceof ,如果該表達(dá)式為true,則可以安全轉(zhuǎn)換。
public class TestAnimal { public static void main(String[] args) { Cat cat = new Cat("元寶",2); Dog dog = new Dog("小七", 1); // 向上轉(zhuǎn)型 Animal animal = cat; animal.eat(); animal = dog; animal.eat(); if(animal instanceof Cat){ cat = (Cat)animal; cat.mew(); } if(animal instanceof Dog){ dog = (Dog)animal; dog.bark(); } } }
3.5、多態(tài)優(yōu)缺點(diǎn)
【使用多態(tài)的好處】
1. 能夠降低代碼的 “圈復(fù)雜度”, 避免使用大量的 if - else
就曉得了什么叫 “圈復(fù)雜度” ?那么我們來(lái)對(duì)比一下
這是if-else分支語(yǔ)句:
public static void drawShapes() { Rect rect = new Rect(); Cycle cycle = new Cycle(); Flower flower = new Flower(); String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"}; for (String shape : shapes) { if (shape.equals("cycle")) { cycle.draw(); } else if (shape.equals("rect")) { rect.draw(); } else if (shape.equals("flower")) { flower.draw(); } } }
如果使用使用多態(tài):
public static void drawShapes() { // 我們創(chuàng)建了一個(gè) Shape 對(duì)象的數(shù)組. Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()}; for (Shape shape : shapes) { shape.draw(); } }
2. 可擴(kuò)展能力更強(qiáng)
如果要新增一種新的形狀, 使用多態(tài)的方式代碼改動(dòng)成本也比較低.
class Triangle extends Shape { @Override public void draw() { System.out.println("△"); } }
對(duì)于類的調(diào)用者來(lái)說(shuō)(drawShapes方法), 只要?jiǎng)?chuàng)建一個(gè)新類的實(shí)例就可以了, 改動(dòng)成本很低. 而對(duì)于不用多態(tài)的情況, 就要把drawShapes 中的 if - else 進(jìn)行一定的修改, 改動(dòng)成本更高.
多態(tài)缺陷:代碼的運(yùn)行效率降低。
3.6、避免在構(gòu)造方法中調(diào)用重寫的方法
我們創(chuàng)建兩個(gè)類, B 是父類, D 是子類. D 中重寫 func 方法. 并且在 B 的構(gòu)造方法中調(diào)用 func
class B { public B() { // do nothing func(); } public void func() { System.out.println("B.func()"); } } class D extends B { private int num = 1; @Override public void func() { System.out.println("D.func() " + num); } } public class Test { public static void main(String[] args) { D d = new D(); } }
執(zhí)行結(jié)果
D.func() 0
1. 構(gòu)造 D 對(duì)象的同時(shí), 會(huì)調(diào)用 B 的構(gòu)造方法.
2. B 的構(gòu)造方法中調(diào)用了 func 方法, 此時(shí)會(huì)觸發(fā)動(dòng)態(tài)綁定, 會(huì)調(diào)用到 D 中的 func此時(shí) D 對(duì)象自身還沒(méi)有構(gòu)造, 此時(shí) num 處在未初始化的狀態(tài), 值為 0.
結(jié)論: “用盡量簡(jiǎn)單的方式使對(duì)象進(jìn)入可工作狀態(tài)”, 盡量不要在構(gòu)造器中調(diào)用方法(如果這個(gè)方法被子類重寫, 就會(huì)觸 發(fā)動(dòng)態(tài)綁定,
但是此時(shí)子類對(duì)象還沒(méi)構(gòu)造完成), 可能會(huì)出現(xiàn)一些隱藏的但是又極難發(fā)現(xiàn)的問(wèn)題.
到此這篇關(guān)于Java超詳細(xì)講解繼承和多態(tài)的使用的文章就介紹到這了,更多相關(guān)Java繼承和多態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA導(dǎo)入外部項(xiàng)目報(bào)Error:java: 無(wú)效的目標(biāo)發(fā)行版: 11的解決方法
這篇文章主要介紹了IDEA導(dǎo)入外部項(xiàng)目報(bào)Error:java: 無(wú)效的目標(biāo)發(fā)行版: 11,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Java數(shù)據(jù)結(jié)構(gòu)超詳細(xì)分析二叉搜索樹
二叉搜索樹是以一棵二叉樹來(lái)組織的。每個(gè)節(jié)點(diǎn)是一個(gè)對(duì)象,包含的屬性有l(wèi)eft,right,p和key,其中,left指向該節(jié)點(diǎn)的左孩子,right指向該節(jié)點(diǎn)的右孩子,p指向該節(jié)點(diǎn)的父節(jié)點(diǎn),key是它的值2022-03-03Java圖形用戶界面設(shè)計(jì)(Swing)的介紹
看到多數(shù)人提到 Java 就以為是網(wǎng)絡(luò)開發(fā),其實(shí)不是這樣的,Java 也可以開發(fā)應(yīng)用程序,而且可以開發(fā)出漂亮的圖形用戶界面的應(yīng)用程序,因此,我寫下這篇文章,希望能帶你進(jìn)入 Java 圖形用戶界面設(shè)計(jì)之門。2016-07-07java EasyExcel實(shí)現(xiàn)動(dòng)態(tài)列解析和存表
這篇文章主要為大家介紹了java EasyExcel實(shí)現(xiàn)動(dòng)態(tài)列解析和存表示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06使用spring框架中的組件發(fā)送郵件功能說(shuō)明
Spring使用的是基本的JavaBean來(lái)完成以前只可能由EJB完成的事情。這篇文章主要介紹了使用spring框架中的組件發(fā)送郵件,需要的朋友可以參考下2017-11-11springboot 中異步任務(wù),定時(shí)任務(wù),郵件任務(wù)詳解
這篇文章主要介紹了springboot 與異步任務(wù),定時(shí)任務(wù),郵件任務(wù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09Java界面編程實(shí)現(xiàn)界面跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了Java界面編程實(shí)現(xiàn)界面跳轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06