一、繼承
1、概念
繼承(inheritance)機(jī)制:是面向?qū)ο蟪绦蛟O(shè)計使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特 性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加新功能,這樣產(chǎn)生新的類,稱派生類。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計的層次結(jié)構(gòu), 體現(xiàn)了由簡單到復(fù)雜的認(rèn)知過程。
繼承主要解決的問題是:共性的抽取,實現(xiàn)代碼復(fù)用。
例如:狗和貓都是動物,分別定義一個貓類和狗類,他們都是動物,讓他們來繼承動物類,那么我們就可以將共性的內(nèi)容進(jìn)行抽取,然后采用繼承的思想來達(dá)到共用。
// Dog.java public class Dog{ string name; int age; float weight; public void eat(){ System.out.println(name + "正在吃飯"); } public void sleep(){ System.out.println(name + "正在睡覺"); } void Bark(){ System.out.println(name + "汪汪汪~~~"); } } // Cat.Java public class Cat{ string name; int age; float weight; public void eat(){ System.out.println(name + "正在吃飯"); } public void sleep() { System.out.println(name + "正在睡覺"); } void mew(){ System.out.println(name + "喵喵喵~~~"); } }
2、語法
在Java中如果要表示類之間的繼承關(guān)系,需要借助extends關(guān)鍵字,具體如下:
修飾符 class 子類 extends 父類 { // ... }
那么對于貓和狗的場景,我們讓貓類和狗類均繼承自動物類:
public class Animal{ String name; int age; public void eat(){ System.out.println(name + "正在吃飯"); } public void sleep(){ System.out.println(name + "正在睡覺"); } } // Dog.java public class Dog extends Animal{ void bark(){ System.out.println(name + "汪汪汪~~~"); } } // Cat.Java public class Cat extends Animal{ void mew(){ System.out.println(name + "喵喵喵~~~"); } } // TestExtend.java public class TestExtend { public static void main(String[] args) { Dog dog = new Dog(); // dog類中并沒有定義任何成員變量,name和age屬性肯定是從父類Animal中繼承下來的 System.out.println(dog.name); System.out.println(dog.age); // dog訪問的eat()和sleep()方法也是從Animal中繼承下來的 dog.eat(); dog.sleep(); dog.bark(); } }
注意事項:
1、子類會將父類中的成員變量或成員方法繼承到子類中去。
2、子類中一定要含有與父類不同的成員方法或者成員變量,體現(xiàn)出與父類的不同,否則繼承就失去了意義。
3、父類成員的訪問
(1)子類中訪問父類成員變量
第一種情況:子類和父類不存在同名成員變量
public class Base { int a; int b; } public class Derived extends Base { int c; int d; public void method(){ a=10;//訪問的是從父類繼承下來的成員變量a b=20;//訪問的是從父類繼承下來的成員變量b c=30;//訪問的是子類自己的成員變量c d=40;//訪問的是子類自己的成員變量d } }
第二種情況:子類和父類成員變量有同名
public class Base { int a; int b; } public class Derived extends Base { int b; int c; public void method(){ a=10; b=20;//此時訪問的是父類中的b還是子類中的b? c=30; //d=40; 編譯報錯,因為在子類和父類中都不存在成員變量d } }
注意事項:
在子類方法中或者通過子類對象訪問成員變量時:
1、如果訪問的成員變量子類中有,優(yōu)先訪問自己的成員變量。
2、如果訪問的成員變量子類中無,則訪問父類繼承下來的,如果父類也沒有定義,則編譯報錯。
3、如果訪問的成員變量與父類中成員變量同名,則優(yōu)先訪問自己的,即:子類將父類同名成員隱藏了。
(2)子類中訪問父類成員方法
第一種情況:子類和父類中不存在同名成員方法
public class Base { public void methodA(){ System.out.println("我是父類成員方法"); } } public class Derived extends Base { public void methodB(){ System.out.println("我是子類成員方法"); } public void methodC(){ methodA();//訪問繼承自父類成員方法 methodB();//訪問子類自己的成員方法 //methodD(); 編譯報錯,在繼承體系中沒有methodD方法 } }
第二種情況:子類和父類中存在同名方法
public class Base { public void methodA(){ System.out.println("我是父類成員方法methodA"); } public void methodB(){ System.out.println("我是父類成員方法methodB"); } } public class Derived extends Base { public void methodA(int a){ System.out.println("我是子類成員方法methodA"); } public void methodB(){ System.out.println("我是子類成員方法methodB"); } public void methodC(){ methodA(1);//帶參的methodA方法,訪問的是子類的方法 methodA();//無參的methodA方法,訪問的是父類的方法 methodB();//直接訪問methodB,訪問到的是子類的方法 } }
注意事項:
1、通過子類對象或方法訪問父類與子類中不同名方法時,優(yōu)先在子類中找,找到則訪問,否則在父類中找,找到則訪問,否則編譯報錯。
2、通過子類對象訪問父類與子類同名方法時,如果父類和子類同名方法的參數(shù)列表不同,根據(jù)調(diào)用方法時傳遞的參數(shù)選擇合適的方法訪問,如果沒有則報錯;如果父類和子類同名方法的原型一致,則只能訪問到子類的,父類的無法通過派生類對象直接訪問到。
咦????那么問題來了,如果想要訪問與子類方法同名且原型一致的父類方法時該怎么訪問呢?――super關(guān)鍵字
4、super關(guān)鍵字
當(dāng)子類和父類中存在同名且原型一致的方法時,我們想要調(diào)用父類的該方法時發(fā)現(xiàn)不能直接訪問,而在Java中提供了super關(guān)鍵字,該關(guān)鍵字的主要作用就是:在子類方法中訪問父類成員。
public class Base { int a; int b; public void methodA(){ System.out.println("我是父類成員方法methodA"); } public void methodB(){ System.out.println("我是父類成員方法methodB"); } } public class Derived extends Base { int a;//與父類同名成員 String b;//與父類同名成員的類型不同 // 與父類中methodA()構(gòu)成重載 public void methodA(int a){ System.out.println("我是子類成員方法methodA"); } //重寫父類中的methodB方法 public void methodB(){ System.out.println("我是子類成員方法methodB"); } public void methodC(){ a=100; super.a=110; b="abc"; super.b=100; methodA(1);//帶參的methodA方法,訪問的是子類的方法 methodA();//無參的methodA方法,訪問的是父類的方法 methodB();//直接訪問methodB,訪問到的是子類的方法 super.methodB();//通過super關(guān)鍵字調(diào)用的是父類的methodB } }
注意事項:
1、只能在非靜態(tài)方法中使用。
2、用于在子類方法中,訪問父類的成員變量和方法。
5、子類構(gòu)造方法
子類對象構(gòu)造時,需要先調(diào)用基類構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。
public class Base { public Base(){ System.out.println("我是父類構(gòu)造方法"); } } public class Derived extends Base { public Derived(){ // super(); // 注意子類構(gòu)造方法中默認(rèn)會調(diào)用基類的無參構(gòu)造方法:super() //當(dāng)用戶沒有寫時編譯器自動添加 //且super()必須是第一條語句,且只能出現(xiàn)一次 System.out.println("我是子類構(gòu)造方法"); } public static void main(String[] args) { Derived a=new Derived(); } }
/*
執(zhí)行結(jié)果
我是父類構(gòu)造方法
我是子類構(gòu)造方法
*/
說明:在子類構(gòu)造方法中,并沒有寫任何關(guān)于基類構(gòu)造的代碼,但是在構(gòu)造子類對象時,先執(zhí)行基類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法,因為:子類對象是一個父類對象,在構(gòu)造子類對象時,先要將從父類繼承下來的成員初始化完整,然后再初始化子類自己新增加的成員。
從對象模型的角度來看:
注意事項:
1、若父類顯式定義無參或者默認(rèn)的構(gòu)造方法,編譯器會給子類生成一個默認(rèn)的構(gòu)造方法,且在子類構(gòu)造方法第一行默認(rèn)有隱含的super()調(diào)用,即調(diào)用基類構(gòu)造方法。
2、如果父類構(gòu)造方法是帶有參數(shù)的,此時編譯器不會再給子類生成默認(rèn)的構(gòu)造方法,此時需要用戶為子類顯式定義構(gòu)造方法,并在子類構(gòu)造方法中選擇合適的父類構(gòu)造方法調(diào)用,否則編譯失敗。
具體看如下代碼:
public class Base { int a; int b; public Base(int a){ //父類中帶參數(shù)的構(gòu)造方法 this.a=a; } } public class Derived extends Base { public Derived(int a,int b){ //此時需要在子類中自定義構(gòu)造方法 super(a);//選擇合適的父類構(gòu)造方法調(diào)用 this.b=b; } public static void main(String[] args) { Derived a=new Derived(10,20); System.out.println(a.a); System.out.println(a.b); } }
/*
執(zhí)行結(jié)果:
10
20
*/
3、在子類構(gòu)造方法中,super(…)調(diào)用父類構(gòu)造時,必須是子類構(gòu)造函數(shù)中第一條語句。
4、super(…)只能在子類構(gòu)造方法中出現(xiàn)一次,并且不能和this同時出現(xiàn)。
6、super和this
相同點:
1、都是Java中的關(guān)鍵字
2、只能在類的非靜態(tài)方法中使用,用來訪問非靜態(tài)成員方法和字段
3、在構(gòu)造方法中調(diào)用時,必須是構(gòu)造方法中的第一條語句,并且不能同時存在,如下圖所示:
不同點:
先給一段代碼
public class Base { int a; int b; } public class Derived extends Base { int c; int d; public void method(){ super.a=10; super.b=20; this.c=30; this.d=40; } public static void main(String[] args) { Derived d=new Derived(); d.method(); } }
1、 this是當(dāng)前對象的引用,當(dāng)前對象即調(diào)用實例方法的對象,super相當(dāng)于是父類對象的引用。針對上述代碼,如下圖所示:
2、在非靜態(tài)成員方法中,this用來訪問本類的方法和屬性,super用來訪問父類繼承下來的方法和屬性。上圖也可以進(jìn)行說明。
3、this是非靜態(tài)成員方法的一個隱藏參數(shù),super不是隱藏的參數(shù)。還是針對上述代碼,從字節(jié)碼的角度來看,打開method方法中的局部變量表,發(fā)現(xiàn)只有this,而沒有super。
4、成員方法中直接訪問本類成員時,編譯之后會將this還原,即本類非靜態(tài)成員都是通過this來訪問的;在子類中如果通過super訪問父類成員,編譯之后在字節(jié)碼層面super實際是不存在的。
5、在構(gòu)造方法中:this(…)用于調(diào)用本類構(gòu)造方法,super(…)用于調(diào)用父類構(gòu)造方法,兩種調(diào)用不能同時在構(gòu)造方法中出現(xiàn)。
6、 構(gòu)造方法中一定會存在super(…)的調(diào)用,用戶沒有寫編譯器也會增加,但是this(…)用戶不寫則沒有。
7、代碼塊執(zhí)行順序
第一種:在沒有繼承關(guān)系時的執(zhí)行順序
public class Person { int age; String name; public Person(int age,String name){ this.age=age; this.name=name; System.out.println("執(zhí)行構(gòu)造方法"); } { System.out.println("執(zhí)行實例代碼塊"); } static { System.out.println("執(zhí)行靜態(tài)代碼塊"); } public static void main(String[] args) { Person a=new Person(20,"luka"); System.out.println("-----------------------"); Person b=new Person(21,"stepth"); } }
/*
執(zhí)行結(jié)果:
執(zhí)行靜態(tài)代碼塊
執(zhí)行實例代碼塊
執(zhí)行構(gòu)造方法
-----------------------
執(zhí)行實例代碼塊
執(zhí)行構(gòu)造方法
*/
說明:
1、靜態(tài)代碼塊先執(zhí)行,并且只執(zhí)行一次,在類加載階段執(zhí)行
2、 當(dāng)有對象創(chuàng)建時,才會執(zhí)行實例代碼塊,實例代碼塊執(zhí)行完成后,最后構(gòu)造方法執(zhí)行
第二種:在存在繼承關(guān)系時的執(zhí)行順序
public class Person { int age; String name; public Person(int age, String name) { this.age = age; this.name = name; System.out.println("Person:執(zhí)行構(gòu)造方法"); } { System.out.println("Person:執(zhí)行實例代碼塊"); } static { System.out.println("Person:執(zhí)行靜態(tài)代碼塊"); } } public class Student extends Person{ public Student(int age,String name){ super(age,name); System.out.println("Student:執(zhí)行構(gòu)造方法"); } { System.out.println("Student:執(zhí)行實例代碼塊"); } static{ System.out.println("Student:執(zhí)行靜態(tài)代碼塊"); } public static void main(String[] args) { Student a=new Student(20,"luka"); System.out.println("------------------------"); Student b=new Student(21,"stepth"); } }
/*
執(zhí)行結(jié)果:
Person:執(zhí)行靜態(tài)代碼塊
Student:執(zhí)行靜態(tài)代碼塊
Person:執(zhí)行實例代碼塊
Person:執(zhí)行構(gòu)造方法
Student:執(zhí)行實例代碼塊
Student:執(zhí)行構(gòu)造方法
------------------------
Person:執(zhí)行實例代碼塊
Person:執(zhí)行構(gòu)造方法
Student:執(zhí)行實例代碼塊
Student:執(zhí)行構(gòu)造方法
*/
說明:
1、父類靜態(tài)代碼塊優(yōu)先于子類靜態(tài)代碼塊執(zhí)行,靜態(tài)代碼塊相較于其他是最早執(zhí)行。
2、父類實例代碼塊和父類構(gòu)造方法緊接著執(zhí)行。
3、子類的實例代碼塊和子類構(gòu)造方法緊接著再執(zhí)行。
4、第二次實例化子類對象時,父類和子類的靜態(tài)代碼塊都將不會再執(zhí)行(靜態(tài)代碼塊只執(zhí)行一次)。
8、父類成員在子類中的可見性
父類中不同訪問權(quán)限的成員,在子類中的可見性又是什么樣子的?
// extend01包中 public class B { private int a; protected int b; public int c; int d; } // extend01包中 // 同一個包中的子類 public class D extends B{ public void method(){ // super.a = 10; // 編譯報錯,父類private成員在相同包子類中不可見 super.b = 20; // 父類中protected成員在相同包子類中可以直接訪問 super.c = 30; // 父類中public成員在相同包子類中可以直接訪問 super.d = 40; // 父類中默認(rèn)訪問權(quán)限修飾的成員在相同包子類中可以直接訪問 } } // extend02包中 // 不同包中的子類 public class C extends B { public void method(){ // super.a = 10; // 編譯報錯,父類中private成員在不同包子類中不可見 super.b = 20; // 父類中protected修飾的成員在不同包子類中可以直接訪問 super.c = 30; // 父類中public修飾的成員在不同包子類中可以直接訪問 //super.d = 40; // 父類中默認(rèn)訪問權(quán)限修飾的成員在不同包子類中不能直接訪問 } } // extend02包中 // 不同包中的類 public class TestC { public static void main(String[] args) { C c = new C(); c.method(); // System.out.println(c.a); // 編譯報錯,父類中private成員在不同包其他類中不可見 // System.out.println(c.b); // 父類中protected成員在不同包其他類中不能直接訪問 System.out.println(c.c); // 父類中public成員在不同包其他類中可以直接訪問 // System.out.println(c.d); // 父類中默認(rèn)訪問權(quán)限修飾的成員在不同包其他類中不能直接訪問 } }
**注意:**父類中private成員變量隨時在子類中不能直接訪問,但是也繼承到子類中了。
9、繼承方式
Java中支持以下幾種繼承方式:
單繼承:
多層繼承:
不同類繼承同一個類
不支持多繼承
10、final關(guān)鍵字
final關(guān)鍵可以用來修飾變量、成員方法以及類。
1、修飾變量或字段,表示常量(即不能修改)。
final int a = 10; a = 20; // 編譯出錯
2、修飾類:表示此類不能被繼承。
final public class Animal { ... } public class Bird extends Animal { ... } // 編譯出錯
平時使用的String字符串類,打開其源碼可以看到它是被final修飾的,不可以被繼承。
3.、修飾方法:表示該方法不能被重寫。
11、組合
組合也是一種表達(dá)類之間關(guān)系的方式, 也是能夠達(dá)到代碼重用的效果。組合并沒有涉及到特殊的語法(諸如 extends 這樣的關(guān)鍵字), 僅僅是將一個類的實例作為另外一個類的字段。
這是繼承和組合的區(qū)別。
//車閘類 class Brake{ } //車把手類 class Handle{ } //輪胎類 class Tire{ } public class Bicycle { private Brake brake; //可以復(fù)用車閘中的屬性和方法 private Handle handle; //可以復(fù)用車把手中的屬性和方法 private Tire tire; //可以復(fù)用輪胎中的屬性和方法 }
作者:luka.lh
over!!!
到此這篇關(guān)于Java中的重要核心知識點之繼承詳解的文章就介紹到這了,更多相關(guān)Java 繼承內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/m0_58715346/article/details/120734408