面向?qū)ο笕蠡咎卣髦豪^承
繼承概述
繼承是類與類之間的一種關(guān)系
多個類繼承單獨的某個類孤页,多個類就可使用單獨的這個類的屬性和行為了;Java中子類更強大
多個類稱為子類(又稱派生類)涩馆,單獨的這個類稱為父類(又稱基類行施,或超類)
-
使用extends關(guān)鍵字實現(xiàn)繼承:public class 子類名 extends 父類名()
public class Student extends People{} //表示Student類繼承自People類
使用繼承的好處:提高代碼復(fù)用,減少代碼冗余魂那,增強類的功能擴展性
-
測試代碼:
package com.java.test; public class Student2 extends People { public void study() { System.out.println(getName() + "讀書學(xué)習(xí)"); } }
package com.java.test; public class Teacher extends People { public void teach() { System.out.println(getName() + "教書育人"); } }
package com.java.test; public class People { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
package com.java.test; public class TestExtends { public static void main(String[] args) { Student2 student = new Student2(); Teacher teacher = new Teacher(); student.setName("徠卡"); student.setAge(23); teacher.setName("哈蘇"); teacher.setAge(32); System.out.println(student.getName() + "\t" + student.getAge()); System.out.println(teacher.getName() + "\t" + teacher.getAge()); student.study(); teacher.teach(); } }
繼承的設(shè)計規(guī)范蛾号,內(nèi)存運行原理
繼承設(shè)計規(guī)范
-
子類們相同特征(共性屬性,共性方法)放在父類中定義涯雅;子類獨有的屬性和行為應(yīng)該定義在子類自己里面
如果子類獨有的屬性鲜结,行為定義在父類中,會導(dǎo)致其他子類也會得到這些屬性和行為活逆,這不符合面向?qū)ο筮壿?/p>
內(nèi)存運行原理
- 首先加載主方法時會在堆內(nèi)存中開辟空間精刷,在空間中劃分出一塊父類空間(稱為super)和一塊子類空間(稱為this)
- image.png
繼承的特點(面試熱點)
-
子類可以繼承父類的屬性和行為,但是子類不能繼承父類的構(gòu)造器
子類有自己的構(gòu)造器蔗候,父類構(gòu)造器用于初始化父類對象
-
子類是否可以繼承父類私有的成員(存在爭議)
Java官方文檔的解釋:子類不能繼承父類的私有成員贬养,但是如果子類中公有的方法影響到了父類私有成員,那么私有成員是能夠被子類使用的
個人理解:當(dāng)存在一個子類對象時琴庵,在內(nèi)存空間上:子類對象繼承的super中私有成員被繼承但是無法直接訪問
image.png
-
子類是否可以繼承父類的靜態(tài)成員(存在爭議)
-
子類可以直接使用父類的靜態(tài)成員(共享并非繼承)
靜態(tài)的成員屬于類本身误算,且靜態(tài)只會加載一次,靜態(tài)是屬于父類的關(guān)系迷殿,即子類不能繼承父類的靜態(tài)成員儿礼,這種關(guān)系是父類向子類共享靜態(tài)成員,子類可以共享訪問使用靜態(tài)成員庆寺,但是并非繼承關(guān)系
-
Java是單繼承模式蚊夫,一個類只能繼承一個直接父類
Java不支持多繼承,但是支持多層繼承
Java中所有的類都是Object類的子類
繼承后:成員變量懦尝,成員方法的訪問特點
-
在子類方法中訪問成員變量知纷,成員方法時,滿足:就近原則
就近原則:
- 先子類局部范圍找
- 然后子類成員范圍找
- 然后父類成員范圍找陵霉,如果父類范圍還沒有找到則報錯
使用super關(guān)鍵字訪問父類的成員
當(dāng)子類和父類中出現(xiàn)了同名的方法琅轧,會優(yōu)先使用子類同名方法,此時要使用父類中的同名方法的辦法是在子類中建立中轉(zhuǎn)方法
-
測試代碼
package com.java.test; public class ExtendsDemo { public static void main(String[] args) { Wolf w = new Wolf(); System.out.println(w.name); w.showName(); w.dance(); //觸發(fā)子類的dance方法 // 調(diào)用中轉(zhuǎn)方法來觸發(fā)父類的dance方法 w.sing(); } } class Animal { public String name = "觸發(fā)父類"; public void dance() { System.out.println("父類dance方法觸發(fā)"); } } class Wolf extends Animal { public String name = "觸發(fā)子類"; public void showName() { String name = "局部名稱"; System.out.println(name); // 局部名稱 System.out.println(this.name); // 觸發(fā)子類 System.out.println(super.name); // 觸發(fā)父類 } public void dance() { System.out.println("子類dance方法觸發(fā)"); } public void sing() { super.dance(); //sing為中轉(zhuǎn)方法 } }
繼承后:方法重寫
在繼承體系中踊挠,子類出現(xiàn)了和父類中一模一樣的方法聲明乍桂,我們稱子類這個方法是重寫的方法
-
方法重寫的應(yīng)用場景:
- 當(dāng)子類需要父類的功能,但父類的該功能不完全滿足自己的需求時
- 子類可以重寫父類中的方法
-
@Override重寫注解:
- 放在重寫后的方法上,作為重寫是否正確的校驗注解
- 加上該注解后如果重寫錯誤睹酌,編譯階段會出現(xiàn)錯誤提示(例如方法名不一致時权谁,可以防止運行的是新方法而不是重寫)
- 建議重寫方法都加上@Override注解,代碼安全憋沿,優(yōu)雅旺芽!
-
方法重寫注意事項和要求
重寫方法的名稱,形參列表必須與被重寫方法的名稱和參數(shù)列表一致
私有方法被能重寫(因為私有方法本身也不可以被訪問)
-
子類重寫方法時辐啄,訪問權(quán)限必須大于或等于父類
按公開級別從大到小依次是:public > protected > 缺省
-
子類不能重寫父類的靜態(tài)方法采章,如果重寫會報錯
因為靜態(tài)方法屬于類,而重寫的前提就是對被重寫的方法擁有所有權(quán)则披,子類不曾擁有父類靜態(tài)方法共缕,所以對子類而言不存在對父類靜態(tài)方法重寫這個說法
-
測試代碼:
package com.java.test; public class ReWrite { public void run() { System.out.println("跑步"); } public void play() { System.out.println("打球"); } public static void main(String[] args) { ReWrite2 re = new ReWrite2(); re.run(); re.play(); } } class ReWrite2 extends ReWrite { @Override public void run() { super.run(); //重寫時保留父類原方法中的功能 System.out.println("馬拉松"); //對父類中的方法進行重寫 } @Override public void play() { super.play(); System.out.println("錦標賽"); } }
繼承后:子類構(gòu)造器的特點
-
子類中所有的構(gòu)造器默認都會先訪問父類中無參的構(gòu)造器洗出,再執(zhí)行自己
- 子類在初始化的時候士复,有可能會使用到父類中的數(shù)據(jù),如果父類沒有完成初始化翩活,子類將無法使用父類的數(shù)據(jù)
- 子類初始化之前阱洪,一定要調(diào)用父類構(gòu)造器先完成父類數(shù)據(jù)空間的初始化
-
如何調(diào)用父類構(gòu)造器
- 子類構(gòu)造器的第一行語句默認都是:super(),不寫也存在
-
測試代碼:
package com.java.test; public class constructor { public static void main(String[] args) { worker w = new worker(); /* 父類構(gòu)造器被觸發(fā) 子類構(gòu)造器被觸發(fā) */ } } class boss { public boss() { System.out.println("父類構(gòu)造器被觸發(fā)"); } } class worker extends boss { public worker() { super(); //默認的菠镇,寫不寫都有冗荸,默認就是找父類無參構(gòu)造器 System.out.println("子類構(gòu)造器被觸發(fā)"); } }
繼承后:子類構(gòu)造器訪問父類有參構(gòu)造器
-
super調(diào)用父類有參構(gòu)造器的作用:
- 初始化繼承自父類的數(shù)據(jù)
-
父類中沒有無參構(gòu)造器,只有有參構(gòu)造器利耍,會出現(xiàn)什么情況
- 報錯蚌本,因為子類默認調(diào)用父類的無參構(gòu)造器
-
如何解決
- 子類構(gòu)造器中可以通過書寫super(...),手動調(diào)用父類的有參構(gòu)造器
-
測試代碼:
package com.java.test; public class Constructor1 { public static void main(String[] args) { American a = new American("張三", 21, "男"); System.out.println(a.getName()); System.out.println(a.getAge()); System.out.println(a.getSex()); } } class English { private String name; private int age; // 有參構(gòu)造器引發(fā)繼承報錯隘梨,選擇了有參構(gòu)造器時程癌,建議加上無參構(gòu)造器 // 或者子類手動調(diào)用父類的有參構(gòu)造器,即便如此轴猎,實際開發(fā)仍建議加上無參構(gòu)造器 public English(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class American extends English { private String sex; // 手動調(diào)用父類的有參構(gòu)造器 public American(String name, int age, String sex) { super(name, age); this.sex = sex; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
this嵌莉,super使用總結(jié)
關(guān)鍵字 | 訪問成員變量 | 訪問成員方法 | 訪問構(gòu)造方法 |
---|---|---|---|
this | this.成員變量 訪問本類成員變量 |
this.成員方法 訪問本類成員方法 |
this(...) 訪問本類構(gòu)造器 |
super | super.成員變量 訪問父類成員變量 |
super.成員方法 訪問父類成員方法 |
super(...) 訪問父類構(gòu)造器 |
- this:代表本類對象的引用
- super:代表父類存儲空間的標識
-
對于this(...)訪問本類構(gòu)造器
案例代碼:
pubic class Student{ private String schoolName; private String name; public Student(String name){ this(name, "奧利奧") } public Student(String name, String schoolName){ this.name = name; this.schoolName = schoolName; } }
- this(...)和super(...)使用注意點
- 子類通過this(...)去調(diào)用本類的其他構(gòu)造器,本類其他構(gòu)造器會通過super手動調(diào)用父類的構(gòu)造器捻脖,最終還是會調(diào)用父類構(gòu)造器的
- this和super都只能放在構(gòu)造器的第一行锐峭,所以也就意味著二者不可以共存在一個構(gòu)造器中