解析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. 子類繼承了父類,重寫(xiě)父類中的方法
1、這種子類包含與父類同名方法的現(xiàn)象被稱為方法重寫(xiě),也被稱為方法覆蓋(Override)??梢哉f(shuō)子類重寫(xiě)了父類的方法,也可以說(shuō)子類覆蓋了父類的方法。
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、注意:方法的重寫(xiě)要遵循“兩同兩小一大”規(guī)則
(1) “兩同”即方法名相同、形參列表相同;
(2) “兩小”指的是子類方法返回值類型應(yīng)比父類方法返回值類型更小或相等,子類方法聲明拋出的異常類應(yīng)比父類方法聲明拋出的異常類更小或相等;
(3) “一大”指的是子類方法的訪問(wèn)權(quán)限應(yīng)比父類方法的訪問(wèn)權(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訪問(wèn)權(quán)限,則該方法對(duì)其子類是隱藏的,因此其子類無(wú)法訪問(wèn)該方法,也就是無(wú)法重寫(xiě)該方法。
如果子類中定義了一個(gè)與父類private方法具有相同的方法名、相同的形參列表、相同的返回值類型的方法,依然不是重寫(xiě),只是在子類中重新定義了一個(gè)新方法。
public class Person {
public int age;
private void show(){
System.out.println("調(diào)用了父類中的show()方法");
}
}
public class Student extends Person {
// show()方法是private方法,子類不可以訪問(wèn)該方法,因此可以添加static關(guān)鍵字
public static void show(){
System.out.println("調(diào)用了子類中的show()方法");
}
}
4、注意:當(dāng)子類覆蓋了父類方法后,訪問(wèn)的便是子類中被覆蓋的方法,將無(wú)法訪問(wèn)父類中被覆蓋的方法。
4. super限定,在子類調(diào)用父類中被覆蓋的方法
1、如果需要在子類方法中調(diào)用父類被覆蓋的實(shí)例方法,則可使用super限定來(lái)調(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限定來(lái)調(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用于限定該對(duì)象調(diào)用它從父類繼承得到的Field或?qū)嵗椒ā?/p>
正如this不能出現(xiàn)在static修飾的方法中一樣,super也不能出現(xiàn)在static修飾的方法中。static修飾的方法是屬于類的,該方法的調(diào)用者可能是一個(gè)類,而不是對(duì)象,因而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ì)訪問(wèn)到子類中定義的Field,無(wú)法訪問(wèn)到父類中被隱藏的Field,在子類定義的實(shí)例方法中可以通過(guò)super來(lái)訪問(wèn)父類中被隱藏的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è)方法中訪問(wèn)名為age的Field,但沒(méi)有顯式指定調(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)用來(lái)完成,在子類構(gòu)造器中調(diào)用父類構(gòu)造器使用super調(diào)用來(lái)完成;
(2) 如果在構(gòu)造器中使用super,則super用于限定該構(gòu)造器初始化的是該對(duì)象從父類繼承得到的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){
// 通過(guò)super調(diào)用父類構(gòu)造器完成初始化過(guò)程
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í)際賦給該變量的對(duì)象決定。如果編譯時(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 = "這是一本好書(shū)";
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();
// 這是一本好書(shū)
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();
// 對(duì)象的Field則不具備多態(tài)性
// 10
System.out.println(p.book);
// 子類中的test方法
p.test();
// 父類中的parent()方法
p.parent();
// Parent類中沒(méi)有sun()方法,編譯報(bào)錯(cuò)
p.sun();
}
}
當(dāng)把一個(gè)子類對(duì)象直接賦給父類引用變量時(shí),例如上面的Parent p = new Sun();,這個(gè)p引用變量的編譯時(shí)類型是Parent,而運(yùn)行時(shí)類型是sun :
(1) Java允許把一個(gè)子類對(duì)象直接賦給一個(gè)父類引用變量,無(wú)須任何類型轉(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) 與方法不同的是,對(duì)象的Field則不具備多態(tài)性,通過(guò)引用變量來(lái)訪問(wèn)其包含的實(shí)例Field時(shí),系統(tǒng)總是試圖訪問(wèn)它編譯時(shí)類型所定義的Field,而不是它運(yùn)行時(shí)類型所定義的Field;
3. 引用變量的強(qiáng)制類型轉(zhuǎn)換
編寫(xiě)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)行,這里所說(shuō)的數(shù)值類型包括整數(shù)型、字符型和浮點(diǎn)型。但數(shù)值類型和布爾類型之間不能進(jìn)行類型轉(zhuǎn)換。
(2) 引用類型之間的轉(zhuǎn)換只能在具有繼承關(guān)系的兩個(gè)類型之間進(jìn)行,如果是兩個(gè)沒(méi)有任何繼承關(guān)系的類型,則無(wú)法進(jìn)行類型轉(zhuǎn)換,否則編譯時(shí)就會(huì)出現(xiàn)錯(cuò)誤。
考慮到進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí)可能出現(xiàn)異常,因此進(jìn)行類型轉(zhuǎn)換之前應(yīng)先通過(guò)instanceof運(yùn)算符來(lái)判斷是否可以成功轉(zhuǎn)換。
(1) instanceof運(yùn)算符的前一個(gè)操作數(shù)通常是一個(gè)引用類型變量,后一個(gè)操作數(shù)通常是一個(gè)類或者接口,它用于判斷前面的對(duì)象是否是后面的類,或者其子類、實(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è)對(duì)象是否是后一個(gè)類的實(shí)例,是否可以成功轉(zhuǎn)換,從而保證代碼更加健壯;
(4) instanceof和(type)是Java提供的兩個(gè)相關(guān)的運(yùn)算符,通常先用instanceof判斷一個(gè)對(duì)象是否可以強(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í)例對(duì)象,而程序調(diào)用的方法在運(yùn)行期才動(dòng)態(tài)綁定,就是引用變量所指向的具體實(shí)例對(duì)象的方法,也就是內(nèi)存里正在運(yùn)行的那個(gè)對(duì)象的方法,而不是引用變量的類型中定義的方法。
2、談?wù)勀銓?duì)多態(tài)的理解?
多態(tài)就是指程序中定義的引用變量所指向的具體類型和通過(guò)該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會(huì)指向哪個(gè)類的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在程序運(yùn)行期間才能決定。
因?yàn)樵诔绦蜻\(yùn)行時(shí)才確定具體的類,這樣,不用修改源代碼,就可以讓引用變量綁定到各種不同的對(duì)象上,從而導(dǎo)致該引用調(diào)用的具體方法隨之改變,即不修改程序代碼就可以改變程序運(yùn)行時(shí)所綁定的具體代碼,讓程序可以選擇多個(gè)運(yùn)行狀態(tài),這就是多態(tài)性。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
一篇文章帶你入門(mé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-08
Spring?Boot項(xiàng)目Jar包加密實(shí)戰(zhàn)教程
本文詳細(xì)介紹了如何在Spring?Boot項(xiàng)目中實(shí)現(xiàn)Jar包加密,我們首先了解了Jar包加密的基本概念和作用,然后學(xué)習(xí)了如何使用Spring?Boot的Jar工具和第三方庫(kù)來(lái)實(shí)現(xiàn)Jar包的加密和解密,感興趣的朋友一起看看吧2024-02-02
Mybatis查不到數(shù)據(jù)查詢返回Null問(wèn)題
Spring Security實(shí)現(xiàn)不同接口安全策略方法詳解

