柚子快報激活碼778899分享:Java 繼承
柚子快報激活碼778899分享:Java 繼承
目錄
什么是繼承
繼承的語法
父類成員的訪問
訪問父類的成員變量
訪問父類的成員方法
super 關(guān)鍵字
訪問父類成員變量
訪問父類成員方法
訪問父類構(gòu)造方法
super 和 this
初始化
訪問限定符
繼承方式
final 關(guān)鍵字
繼承和組合
什么是繼承
在 Java 中,使用類來對現(xiàn)實中的實體進行描述,例如
定義一個 Cat 類來描述貓:
public class Cat {
String name;
int age;
public void eat() {
System.out.println("吃飯");
}
}
定義一個 Dog 類來描述狗:
public class Dog {
String name;
int age;
public void eat() {
System.out.println("吃飯");
}
}
而上述代碼中的 name、age 以及 eat 方法,都是重復的
此時,就可以對這些共性進行抽取
而在面向?qū)ο笏枷胫刑岢隽?繼承 的概念,專門用來進行共性抽取,實現(xiàn)代碼復用
繼承(inhertance):是面向?qū)ο缶幊痰奶卣鳎试S在保持原有類的基礎(chǔ)上進行擴展,增加新的功能,這樣產(chǎn)生的新類,稱之為派生類。通過繼承,能夠?qū)崿F(xiàn)共性的抽取,從而實現(xiàn)代碼的復用
我們將上述 Cat 和 Dog 的共性進行抽取,使用繼承的思想來達到復用效果:
如上圖所示,Dog 和 Cat 都繼承了 Animal 類,其中,Animal 稱為 父類(或 超類、基類),Dog 和 Cat 稱之為 Animal 的 子類(或 派生類),繼承之后,子類可以復用父類中的成員,子類在實現(xiàn)時只需關(guān)心自己新增的成員即可
繼承的語法
在 Java 中若要表示類之間的繼承關(guān)系,需要使用 extends 關(guān)鍵字:
修飾符 class 子類 extends 父類{
? ? ? ? ...
}
public class Animal {
String name;
int age;
public void eat() {
System.out.println("吃飯");
}
}
Cat 類繼承 Animal,還可以新增需要的成員變量或方法
public class Cat extends Animal{
public void miaow() {
System.out.println("喵喵叫");
}
}
子類會將父類中的成員變量或成員方法繼承到子類中
子類在繼承父類之后,可以添加自己特有的成員
父類成員的訪問
子類將父類中的方法和變量繼承之后,那么,子類中能否直接訪問父類中繼承下來的成員呢?
訪問父類的成員變量
當子類和父類不存在同名對象時
public class A {
int a = 10;
}
public class B extends A {
int b = 20;
public static void main(String[] args) {
B b = new B();
System.out.println(b.a);
System.out.println(b.b);
// System.out.println(b.c); // 編譯失敗,子類和父類中都不存在 c
b.method();
}
}
運行結(jié)果:
?
子類自己有就訪問自己的成員變量,若沒有,就訪問父類的成員變量?
當子類和父類存在同名對象時
public class A {
int a = 10;
int b = 20;
}
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
public void method() {
System.out.println("a: " + a + " b: " + b);
}
public static void main(String[] args) {
B b = new B();
b.method();
}
}
運行結(jié)果:
存在同名變量時,優(yōu)先訪問子類的成員變量?
?從上述兩個示例中,可以看出:
若訪問的成員變量子類中有,則訪問子類自己的成員變量
若訪問的成員變量子類中沒有,則訪問父類中繼承下來的,若父類中也沒有,則會編譯報錯
若訪問的成員變量,父類子類中都有,則優(yōu)先訪問自己的成員變量
即 自己有就優(yōu)先自己的,若沒有再向父類中找
訪問父類的成員方法
成員方法名不同
public class A {
int a = 10;
int b = 20;
public void methodA() {
System.out.println("methodA...");
}
}
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
public void methodB() {
System.out.println("methodB...");
}
public static void main(String[] args) {
B b = new B();
b.methodA();
b.methodB();
// b.methodC(); // 編譯失敗,在繼承體系中沒有發(fā)現(xiàn) methodC()
}
}
運行結(jié)果:
當成員方法沒有同名時,在子類方法中或通過子類對象訪問方法時,若自己有,訪問自己的,若自己沒有,則在父類中找,若父類中也沒有,則報錯
成員方法名相同
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
public void methodA(int a) {
System.out.println("methodA..." + a);
}
public static void main(String[] args) {
B b = new B();
b.methodA();
b.methodA(10);
}
}
?運行結(jié)果:
當子類方法名和父類方法名相同,但其參數(shù)列表不同時,就構(gòu)成了重載,根據(jù)調(diào)用方法時傳遞的參數(shù)選擇合適的方法訪問,若不存在該方法,則報錯
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
@Override
public void methodA() {
System.out.println("B methodA...");
}
public static void main(String[] args) {
B b = new B();
b.methodA();
}
}
當子類方法名和父類方法名相同,且其參數(shù)列表也相同時,就構(gòu)成了重寫,此時,會訪問子類的方法
那么,當子類中存在和父類相同的成員時,如何在子類中訪問父類同名成員呢?
super 關(guān)鍵字
當子類和父類中存在相同名稱的成員時,若要在子類方法中訪問父類同名成員時,是不能直接訪問的。Java 提供了 super 關(guān)鍵字,super 的主要作用:在子類方法中訪問父類成員
訪問父類成員變量
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
public void methodB() {
System.out.println(super.a);
System.out.println(super.b);
}
public static void main(String[] args) {
B b = new B();
b.methodB();
}
}
訪問父類成員方法
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
public void methodB() {
super.methodA();
System.out.println("methodB...");
}
public static void main(String[] args) {
B b = new B();
b.methodB();
}
}
需要注意的是:只能在非靜態(tài)方法中使用 super
訪問父類構(gòu)造方法
在子類對象構(gòu)造時,會先調(diào)用父類構(gòu)造方法,然后再執(zhí)行子類構(gòu)造方法
為 A 添加帶一個參數(shù)的構(gòu)造方法
public class A {
int a = 10;
int b = 20;
public A(int a) {
System.out.println("a");
}
public void methodA() {
System.out.println("methodA...");
}
}
?編譯報錯:A 中沒有可用的默認構(gòu)造函數(shù)
為什么會報錯呢?
這是因為在 A 中沒有定義的構(gòu)造方法時,編譯器提供了默認的無參構(gòu)造方法
子類 B 中也沒有定義構(gòu)造方法,編譯器也提供了默認的無參構(gòu)造方法,且在 B 的構(gòu)造方法中第一行默認有隱含的 super() 調(diào)用父類的無參構(gòu)造方法
當添加了帶有一個參數(shù)的構(gòu)造方法后,編譯器也就不再提供無參的構(gòu)造方法,此時也就會報錯
我們可以在 A 中添加無參構(gòu)造方法
或是在 B 的無參構(gòu)造方法中調(diào)用 A 帶有一個參數(shù)的構(gòu)造方法
public B(int a) {
super(a);
}
當父類中有多個構(gòu)造方法時,就需要在子類構(gòu)造方法中選擇一個合適的父類構(gòu)造方法調(diào)用,否則編譯失敗
為什么要先調(diào)用父類的構(gòu)造方法呢?
子類對象中的成員是由兩部分組成的:父類繼承下來的成員 和 子類新增的成員
?
?因此,在構(gòu)造子類對象時,就需要先調(diào)用父類的構(gòu)造方法,將從父類繼承下來的成員構(gòu)造完整,然后再調(diào)用子類自己的構(gòu)造方法,將子類自己新增的成員初始化完整
在子類構(gòu)造方法中,通過 super(...) 調(diào)用父類構(gòu)造,且 super(...) 必須是子類構(gòu)造函數(shù)中的第一條語句
因此,super(...) 只能在子類構(gòu)造方法中出現(xiàn)一次,且必須在第一行
而若需要在構(gòu)造方法中使用 this 關(guān)鍵字調(diào)用類中的其他構(gòu)造方法,也必須在第一行使用,也就是說,super(...) 和 this(...) 不能同時出現(xiàn)
super 和 this
相同點
(1)都是 Java 中的關(guān)鍵字
(2)都可以在成員方法中用來訪問成員變量和調(diào)用其他成員方法,都可以作為構(gòu)造方法的第一條語句
(3)只能在類的非靜態(tài)方法中使用,用來訪問非靜態(tài)成員方法和變量
(4)在構(gòu)造方法中調(diào)用時,必須是構(gòu)造方法的第一條語句,并且不能同時存在
不同點
(1)this 表示當前對象(實例方法的對象)的引用,而 super 表示子類對象重父類繼承下來部分成員的引用
(2)在非靜態(tài)成員方法中,this 用來訪問本類的方法和屬性,super 用來訪問父類繼承下來的方法和屬性
(3)在構(gòu)造方法中,this(...) 用于調(diào)用本類構(gòu)造方法,super(...) 用于調(diào)用父類構(gòu)造方法,兩種調(diào)用不能同時在構(gòu)造方法中出現(xiàn)
(4)構(gòu)造方法中一定會存在 super(...) 的調(diào)用,但 this(...) 若沒有寫則沒有
初始化
在前面的文章 Java 代碼塊-CSDN博客?中,我們學習了代碼塊,重點學習了 實例代碼塊 和 靜態(tài)代碼塊,以及它們的執(zhí)行順序,接下來,我們就來看存在繼承關(guān)系時,它們的執(zhí)行順序
public class A {
int a;
int b;
{
a = 10;
b = 20;
System.out.println("A 構(gòu)造代碼塊執(zhí)行...");
}
static {
System.out.println("A 靜態(tài)代碼塊執(zhí)行...");
}
public A(int a) {
System.out.println("A 構(gòu)造方法執(zhí)行...");
}
public void methodA() {
System.out.println("methodA...");
}
}
public class B extends A {
int a = 100; // 與父類中的成員同名,且類型相同
char b = 'b'; // 與父類中的成員同名,但類型不同
static {
System.out.println("B 靜態(tài)代碼塊執(zhí)行...");
}
{
System.out.println("B 構(gòu)造代碼塊執(zhí)行...");
}
public B(int a) {
super(a);
System.out.println("B 構(gòu)造方法執(zhí)行...");
}
public void methodA() {
System.out.println("methodB...");
}
public static void main(String[] args) {
B b1 = new B(10);
System.out.println("---------------------");
B b2 = new B(20);
}
}
運行結(jié)果:?
?通過運行結(jié)果,可以看到:
靜態(tài)代碼塊先執(zhí)行,并且只執(zhí)行一次,在類加載階段執(zhí)行
父類靜態(tài)代碼塊優(yōu)先于子類靜態(tài)代碼塊執(zhí)行,且最早執(zhí)行
當有對象創(chuàng)建時,才會執(zhí)行實例代碼塊
父類實例代碼塊和父類構(gòu)造方法先執(zhí)行,然后再執(zhí)行子類的實例代碼塊和子類的構(gòu)造方法
訪問限定符
為了實現(xiàn)封裝性,Java 引入了訪問限定符,主要限定:類或類中成員能否在類外或其他包中被訪問
范圍privatedefaultprotectedpublic同一個包中的同一個類√√√√同一個包中的不同類√√√不同包中的子類√√不同包中的非子類√
public:在任何類都可以訪問
protected:在同一個包中的類或不同包中的子類可以訪問
default:同一個包中的類可以訪問
private:只有該類內(nèi)部可以訪問
當 A 和 B 位于同一個包中時:
public class A {
public int a;
int b;
protected int c;
private int d;
}
public class B extends A {
public void method() {
super.a = 10;
super.b = 20;
super.c = 30;
super.d = 40;
}
}
由于變量 d 是 A 私有的,即只能在 A 類中訪問,因此,子類中不能直接訪問
但是,父類中的 private 成員變量雖然不能在子類中直接訪問,但是也繼承到子類中了
子類可以通過父類提供的方法來進行修改
public class A {
public int a;
int b;
protected int c;
private int d;
public int getD() {
return d;
}
public void setD(int d) {
this.d = d;
}
}
public class B extends A {
public void method() {
super.a = 10;
super.b = 20;
super.c = 30;
super.setD(40);
}
}
當 A 和 B 位于不同包中:
public class B extends A {
public void method() {
super.a = 10; // 父類中 public 修飾的成員在不同包子類中可以直接訪問
super.b = 20; // 父類中 protected 修飾的成員在不同包子類中可以直接訪問
// super.c = 30; // 編譯報錯,父類中默認訪問權(quán)限修飾的成員在不同包子類中不能直接訪問
// super.d = 40; // 編譯報錯,父類中默認訪問權(quán)限修飾的成員在不同包子類中不能直接訪問
}
}
繼承方式
在 Java 中,支持的繼承方式有:
(1)單繼承
(2)多層繼承
(3)不同類繼承同一個類
(4)多繼承(不支持)
Java 中不支持多繼承
類是對現(xiàn)實事物的抽象,而當情況比較復雜時,涉及到的類也會比較多,類之間的關(guān)系也會比較復雜
但即使如此,我們并不希望類之間的繼承層次太復雜,一般不希望出現(xiàn)超過三層的繼承關(guān)系
此時,若想從語法上限制繼承,就可以使用 final 關(guān)鍵字
final 關(guān)鍵字
final 可以用來修飾變量、成員方法以及類
當修飾變量時,表示該變量為常量(不可變)
當修飾方法時,表示該方法不能被重寫
當修飾類時,表示該類不能被繼承
繼承和組合
與繼承類似,組合也是一種表達類之間關(guān)系的方式,也能夠達到代碼重用的效果。
但組合并沒有涉及到特殊的語法,僅僅是將一個類的實例作為另一個類的字段
繼承表示的對象之間是 is a 的關(guān)系,如:貓 是 動物
組合表示的對象之間是 has a 的關(guān)系,如:電腦 有 鍵盤
組合:
public class Person {
private Student[] students;
}
此時 Student 屬于?Person 的一部分,? 在 Person 中,可以復用 Student 中的屬性和方法
繼承:?
public class Student extends Person{
}
Student 是 Person 的子類,繼承了父類的成員,能夠復用父類中的屬性和方法
組合和繼承都可以實現(xiàn)代碼復用,是使用組合還是繼承,需要根據(jù)具體的情況來進行選擇
柚子快報激活碼778899分享:Java 繼承
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。