解析JavaSE的繼承和多態(tài)
1. 繼承
1. 子類繼承了父類,獲得父類的全部Field和方法。
子類Student類繼承父類,將可以獲得父類的全部Field和方法
public class Person { public int age; public void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { public static void main(String[] args) { Student student = new Student(); student.name = "張三"; student.age=18; // 調(diào)用父類中的show()方法 student.show(); } }
2. 子類繼承了父類,額外增加新的Field和方法
繼承了父類中的屬性和方法,同時(shí)新增一個(gè)show1()方法
public class Person { public int age; public void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { public static void show1(){ System.out.println("調(diào)用了子類中的show1()方法"); } public static void main(String[] args) { Student student = new Student(); // 調(diào)用父類中的show()方法 student.show(); // 調(diào)用子類中的show1()方法 student.show1(); } }
3. 子類繼承了父類,重寫父類中的方法
1、這種子類包含與父類同名方法的現(xiàn)象被稱為方法重寫,也被稱為方法覆蓋(Override)??梢哉f子類重寫了父類的方法,也可以說子類覆蓋了父類的方法。
public class Person { public int age; public void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { public void show(){ System.out.println("調(diào)用了子類中的show()方法"); } public static void main(String[] args) { Student student = new Student(); // 調(diào)用了子類中的show()方法 student.show(); } }
2、注意:方法的重寫要遵循“兩同兩小一大”規(guī)則
(1) “兩同”即方法名相同、形參列表相同;
(2) “兩小”指的是子類方法返回值類型應(yīng)比父類方法返回值類型更小或相等,子類方法聲明拋出的異常類應(yīng)比父類方法聲明拋出的異常類更小或相等;
(3) “一大”指的是子類方法的訪問權(quán)限應(yīng)比父類方法的訪問權(quán)限更大或相等;
(4) 尤其需要指出的是,覆蓋方法和被覆蓋方法要么都是類方法,要么都是實(shí)例方法,不能一個(gè)是類方法,一個(gè)是實(shí)例方法;
public class Person { public int age; public void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { // 編譯報(bào)錯(cuò),因?yàn)楦割愔械膕how()方法不是static修飾的,子類就不能使用static修飾 public static void show(){ System.out.println("調(diào)用了子類中的show()方法"); } }
3、注意:如果父類方法具有private訪問權(quán)限,則該方法對其子類是隱藏的,因此其子類無法訪問該方法,也就是無法重寫該方法。
如果子類中定義了一個(gè)與父類private方法具有相同的方法名、相同的形參列表、相同的返回值類型的方法,依然不是重寫,只是在子類中重新定義了一個(gè)新方法。
public class Person { public int age; private void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { // show()方法是private方法,子類不可以訪問該方法,因此可以添加static關(guān)鍵字 public static void show(){ System.out.println("調(diào)用了子類中的show()方法"); } }
4、注意:當(dāng)子類覆蓋了父類方法后,訪問的便是子類中被覆蓋的方法,將無法訪問父類中被覆蓋的方法。
4. super限定,在子類調(diào)用父類中被覆蓋的方法
1、如果需要在子類方法中調(diào)用父類被覆蓋的實(shí)例方法,則可使用super限定來調(diào)用父類被覆蓋的實(shí)例方法。
public class Person { public int age; public void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { public void show(){ System.out.println("調(diào)用了子類中的show()方法"); } public void test(){ // 調(diào)用了父類中的show()方法 super.show(); } public static void main(String[] args) { Student student = new Student(); student.test(); } }
2、如果需要在子類方法中調(diào)用父類被覆蓋的類方法,則可使用super限定來調(diào)用父類被覆蓋的類方法。
public class Person { public int age; public static void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { public static void show(){ System.out.println("調(diào)用了子類中的show()方法"); } public void test(){ // 調(diào)用了父類中的show()方法 Person.show(); } public static void main(String[] args) { Student student = new Student(); student.test(); } }
3、super是Java提供的一個(gè)關(guān)鍵字,super用于限定該對象調(diào)用它從父類繼承得到的Field或?qū)嵗椒ā?/p>
正如this不能出現(xiàn)在static修飾的方法中一樣,super也不能出現(xiàn)在static修飾的方法中。static修飾的方法是屬于類的,該方法的調(diào)用者可能是一個(gè)類,而不是對象,因而super限定也就失去了意義。
public class Person { public int age; public void show(){ System.out.println("調(diào)用了父類中的show()方法"); } }
public class Student extends Person { public void show(){ System.out.println("調(diào)用了子類中的show()方法"); } public void test(){ // 調(diào)用了父類中的show()方法 super.show(); } public static void main(String[] args) { Student student = new Student(); student.test(); // 編譯報(bào)錯(cuò),因?yàn)閟uper不能出現(xiàn)在static修飾的方法中 super.show(); } }
4、如果子類定義了和父類同名的Field,則會(huì)發(fā)生子類Field隱藏父類Field的情形。
在正常情況下,子類里定義的方法默認(rèn)會(huì)訪問到子類中定義的Field,無法訪問到父類中被隱藏的Field,在子類定義的實(shí)例方法中可以通過super來訪問父類中被隱藏的Field
public class Person { public int age=5; }
public class Student extends Person { private int age = 10; public void getOwner(){ System.out.println(age); } public void getBase(){ System.out.println(super.age); } public static void main(String[] args) { Student student = new Student(); // 10 student.getOwner(); // 5 student.getBase(); } }
如果在某個(gè)方法中訪問名為age的Field,但沒有顯式指定調(diào)用者,則系統(tǒng)查找a的順序?yàn)椋?/p>
(1) 查找該方法中是否有名為age的局部變量;
(2) 查找當(dāng)前類中是否包含名為age的Field;
(3) 查找age的直接父類中是否包含名為age的Field,依次上溯age的所有父類,直到j(luò)ava.lang.Object類,如果最終不能找到名為age的Field,則系統(tǒng)出現(xiàn)編譯錯(cuò)誤。
5、子類不會(huì)獲得父類的構(gòu)造器,但子類構(gòu)造器里可以調(diào)用父類構(gòu)造器的初始化代碼
(1) 在一個(gè)構(gòu)造器中調(diào)用另一個(gè)重載的構(gòu)造器使用this調(diào)用來完成,在子類構(gòu)造器中調(diào)用父類構(gòu)造器使用super調(diào)用來完成;
(2) 如果在構(gòu)造器中使用super,則super用于限定該構(gòu)造器初始化的是該對象從父類繼承得到的Field,而不是該類自己定義的Field;
public class Person { public String name; public int age; public Person(String name){ this.name = name; } public Person(String name,int age){ this.name = name; this.age = age; } }
public class Student extends Person { private String color; // super調(diào)用的是其父類的構(gòu)造器,而this調(diào)用的是同一個(gè)類中重載的構(gòu)造器 // 使用super調(diào)用父類構(gòu)造器也必須出現(xiàn)在子類構(gòu)造器執(zhí)行體的第一行,所以this調(diào)用和super調(diào)用不會(huì)同時(shí)出現(xiàn) public Student(String name,int age){ super(name); this.age = age; } public Student(String name,int age,String color){ // 通過super調(diào)用父類構(gòu)造器完成初始化過程 this(name,age); this.color = color; } public static void main(String[] args) { Student student = new Student("張三",18,"紅色"); } }
2. 多態(tài)
Java引用變量有兩個(gè)類型:一個(gè)是編譯時(shí)類型,一個(gè)是運(yùn)行時(shí)類型。編譯時(shí)類型由聲明該變量時(shí)使用的類型決定,運(yùn)行時(shí)類型由實(shí)際賦給該變量的對象決定。如果編譯時(shí)類型和運(yùn)行時(shí)類型不一致,就可能出現(xiàn)所謂的多態(tài)。
public class Parent { public int book=10; public void test(){ System.out.println("父類中的test()方法"); } public void parent(){ System.out.println("父類中的parent()方法"); } }
public class Sun extends Parent { public String book = "這是一本好書"; public void test(){ System.out.println("子類中的test方法"); } public void sun(){ System.out.println("子類中的sun方法"); } public static void main(String[] args) { // 編譯時(shí)類型和運(yùn)行時(shí)類型一致,不存在多態(tài) Parent parent = new Parent(); // 10 System.out.println(parent.book); // 父類中的test()方法 parent.test(); // 父類中的parent()方法 parent.parent(); // 編譯時(shí)類型和運(yùn)行時(shí)類型一致,不存在多態(tài) Sun sun = new Sun(); // 這是一本好書 System.out.println(sun.book); // 子類中的test方法 sun.test(); // 子類中的sun方法 sun.sun(); // 編譯時(shí)類型和運(yùn)行時(shí)類型不一致,存在多態(tài) // p變量的編譯時(shí)類型是Parent,而運(yùn)行時(shí)類型是Sun Parent p = new Sun(); // 對象的Field則不具備多態(tài)性 // 10 System.out.println(p.book); // 子類中的test方法 p.test(); // 父類中的parent()方法 p.parent(); // Parent類中沒有sun()方法,編譯報(bào)錯(cuò) p.sun(); } }
當(dāng)把一個(gè)子類對象直接賦給父類引用變量時(shí),例如上面的Parent p = new Sun();,這個(gè)p引用變量的編譯時(shí)類型是Parent,而運(yùn)行時(shí)類型是sun :
(1) Java允許把一個(gè)子類對象直接賦給一個(gè)父類引用變量,無須任何類型轉(zhuǎn)換,這種被稱為向上轉(zhuǎn)型,向上轉(zhuǎn)型由系統(tǒng)自動(dòng)完成;
(2) 當(dāng)調(diào)用該引用變量的test方法時(shí),實(shí)際執(zhí)行的是Sun類中覆蓋后的test方法,這就是多態(tài);當(dāng)運(yùn)行時(shí)調(diào)用該引用變量的方法時(shí),其方法行為總是表現(xiàn)出子類方法的行為特征,而不是父類方法的行為特征,這就可能出現(xiàn):相同類型的變量、調(diào)用同一個(gè)方法時(shí)呈現(xiàn)出多種不同的行為特征,這就是多態(tài)。
(3) 與方法不同的是,對象的Field則不具備多態(tài)性,通過引用變量來訪問其包含的實(shí)例Field時(shí),系統(tǒng)總是試圖訪問它編譯時(shí)類型所定義的Field,而不是它運(yùn)行時(shí)類型所定義的Field;
3. 引用變量的強(qiáng)制類型轉(zhuǎn)換
編寫Java程序時(shí),引用變量只能調(diào)用它編譯時(shí)類型的方法,而不能調(diào)用它運(yùn)行時(shí)類型的方法,如果需要讓這個(gè)引用變量調(diào)用它運(yùn)行時(shí)類型的方法,則必須把它強(qiáng)制類型轉(zhuǎn)換成運(yùn)行時(shí)類型。
(1) 基本類型之間的轉(zhuǎn)換只能在數(shù)值類型之間進(jìn)行,這里所說的數(shù)值類型包括整數(shù)型、字符型和浮點(diǎn)型。但數(shù)值類型和布爾類型之間不能進(jìn)行類型轉(zhuǎn)換。
(2) 引用類型之間的轉(zhuǎn)換只能在具有繼承關(guān)系的兩個(gè)類型之間進(jìn)行,如果是兩個(gè)沒有任何繼承關(guān)系的類型,則無法進(jìn)行類型轉(zhuǎn)換,否則編譯時(shí)就會(huì)出現(xiàn)錯(cuò)誤。
考慮到進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí)可能出現(xiàn)異常,因此進(jìn)行類型轉(zhuǎn)換之前應(yīng)先通過instanceof運(yùn)算符來判斷是否可以成功轉(zhuǎn)換。
(1) instanceof運(yùn)算符的前一個(gè)操作數(shù)通常是一個(gè)引用類型變量,后一個(gè)操作數(shù)通常是一個(gè)類或者接口,它用于判斷前面的對象是否是后面的類,或者其子類、實(shí)現(xiàn)類的實(shí)例。如果是,則返回true,否則返回false;
(2) 在使用instanceof運(yùn)算符時(shí)需要注意:instanceof運(yùn)算符前面操作數(shù)的編譯時(shí)類型要么與后面的類相同,要么與后面的類具有父子繼承關(guān)系,否則會(huì)引起編譯錯(cuò)誤;
(3) instanceof運(yùn)算符的作用是:在進(jìn)行強(qiáng)制類型轉(zhuǎn)換之前,首先判斷前一個(gè)對象是否是后一個(gè)類的實(shí)例,是否可以成功轉(zhuǎn)換,從而保證代碼更加健壯;
(4) instanceof和(type)是Java提供的兩個(gè)相關(guān)的運(yùn)算符,通常先用instanceof判斷一個(gè)對象是否可以強(qiáng)制類型轉(zhuǎn)換,然后再使用(type)運(yùn)算符進(jìn)行強(qiáng)制類型轉(zhuǎn)換,從而保證程序不會(huì)出現(xiàn)錯(cuò)誤;
public class Main { public static void main(String[] args) { Object object = "hello"; if(object instanceof String){ String s = (String) object; } } }
4. 面試題
1、Java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
Java中的多態(tài)靠的是父類或接口定義的引用變量可以指向子類或具體實(shí)現(xiàn)類的實(shí)例對象,而程序調(diào)用的方法在運(yùn)行期才動(dòng)態(tài)綁定,就是引用變量所指向的具體實(shí)例對象的方法,也就是內(nèi)存里正在運(yùn)行的那個(gè)對象的方法,而不是引用變量的類型中定義的方法。
2、談?wù)勀銓Χ鄳B(tài)的理解?
多態(tài)就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會(huì)指向哪個(gè)類的實(shí)例對象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在程序運(yùn)行期間才能決定。
因?yàn)樵诔绦蜻\(yùn)行時(shí)才確定具體的類,這樣,不用修改源代碼,就可以讓引用變量綁定到各種不同的對象上,從而導(dǎo)致該引用調(diào)用的具體方法隨之改變,即不修改程序代碼就可以改變程序運(yùn)行時(shí)所綁定的具體代碼,讓程序可以選擇多個(gè)運(yùn)行狀態(tài),這就是多態(tài)性。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
一篇文章帶你入門java算術(shù)運(yùn)算符(加減乘除余,字符連接)
這篇文章主要介紹了Java基本數(shù)據(jù)類型和運(yùn)算符,結(jié)合實(shí)例形式詳細(xì)分析了java基本數(shù)據(jù)類型、數(shù)據(jù)類型轉(zhuǎn)換、算術(shù)運(yùn)算符、邏輯運(yùn)算符等相關(guān)原理與操作技巧,需要的朋友可以參考下2021-08-08Spring?Boot項(xiàng)目Jar包加密實(shí)戰(zhàn)教程
本文詳細(xì)介紹了如何在Spring?Boot項(xiàng)目中實(shí)現(xiàn)Jar包加密,我們首先了解了Jar包加密的基本概念和作用,然后學(xué)習(xí)了如何使用Spring?Boot的Jar工具和第三方庫來實(shí)現(xiàn)Jar包的加密和解密,感興趣的朋友一起看看吧2024-02-02

Mybatis查不到數(shù)據(jù)查詢返回Null問題

Spring Security實(shí)現(xiàn)不同接口安全策略方法詳解