Java 繼承和多態(tài)的作用及好處
1. 繼承
1.1 什么是繼承
生活中有很多繼承的例子,如兒子繼承父親的家產(chǎn),學(xué)弟繼承學(xué)長的宿舍等,而在Java中繼承就是子類繼承了父類里面的成員,也就相當(dāng)于在父類的基礎(chǔ)上進(jìn)行拓展的內(nèi)容放到子類里面,從簡單到復(fù)雜的過程,而Java里面的繼承是通過extends關(guān)鍵字實(shí)現(xiàn)的。
1.2 繼承的作用和好處
當(dāng)有兩個(gè)類dog類和cat類如下:
public class Dog {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "在吃東西");
}
public void map() {
System.out.println(this.name + "在喵喵叫");
}
}public class Cat {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "在吃東西");
}
public void wap() {
System.out.println(this.name + "在汪汪叫");
}
}通過觀察會發(fā)現(xiàn)上面的dog類和cat類里面有一些共有的成員變量和方法,這時(shí)候我們就在想能不能把這些元素放到同一個(gè)類里面被其他需要的類使用呢?
這時(shí)候就想到了繼承,繼承就可以實(shí)現(xiàn)這一需求,這時(shí)我們再創(chuàng)建一個(gè)類:
public class Test {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "在吃東西");
}
}此時(shí)我們就可以通過關(guān)鍵字extends來實(shí)現(xiàn)將Test類里面的成員用到dog和cat類中。
而通過繼承可以減少代碼的復(fù)用。
1.3 繼承的語法
修飾符 class 子類 extends 父類 { }
上面的cat和dog類就可以通過extends關(guān)鍵字實(shí)現(xiàn)繼承,如下圖:
public class Dog extends Test {
public void map() {
System.out.println(this.name + "在喵喵叫");
}
}public class Cat extends Test {
public void wap() {
System.out.println(this.name + "在汪汪叫");
}
}此時(shí)dog類和cat類就包含了父類里面的成員,在內(nèi)存里也就是:

1.4 子類訪問父類里面的成員變量
1. 子類和父類里面的成員變量不同名
這時(shí)候可以直接訪問:
//父類
public class Base {
int a;
int b;
}
//子類
public class Derived extends Base {
int c;
public void test() {
a = 10;
b = 20;
c = 30;
}
}子類可以直接使用this訪問子類和父類中的成員變量。
2. 子類和父類中存在成員變量同名
//父類
public class Base {
int a;
int b;
}
//子類
public class Derived extends Base {
int c;
int a;
public void test() {
a = 10;
b = 20;
c = 30;
}
}此時(shí)父類和子類都有成員變量a,此時(shí)子類會優(yōu)先訪問子類的變量。
- 當(dāng)子類和父類有相同名字的成員變量時(shí)候時(shí)候,會優(yōu)先訪問子類的成員變量。
- 若子類和父類都沒有的成員變量訪問則會編譯錯(cuò)誤。
1.5 子類訪問父類里面的成員方法
1. 子類和父類方法名字不同。
則子類直接訪問訪問父類的方法。并且子類和父類的方法可以構(gòu)成方法的重載,如下面代碼:
public class Base {
public void sit() {
System.out.println("hehe");
}
}
public class Derived extends Base {
public void print() {
System.out.println("haha");
}
public void sit(int a) {
System.out.println("hehe");
}
public void test() {
print();//調(diào)用子類的方法
//構(gòu)成重載
sit();//調(diào)用父類的方法
sit(10);//調(diào)用子類的方法
}
}當(dāng)子類和父類的方法相同時(shí),則子類調(diào)用父類時(shí),則會就近原則優(yōu)先調(diào)用子類。代碼如下:
public class Base {
public void sit() {
System.out.println("hehe");
}
}
public class Derived extends Base {
public void print() {
System.out.println("haha");
}
public void sit() {
System.out.println("haha");
}
public void test() {
print();//調(diào)用子類的方法
sit();//子類和父類都有該方法會優(yōu)先調(diào)用子類的方法,會打印haha。
}
}1.6 super關(guān)鍵字
上面我們發(fā)現(xiàn)子類和父類當(dāng)中如果出現(xiàn)相同的成員變量或者成員方法時(shí),子類調(diào)用該成員時(shí)會優(yōu)先調(diào)用子類的成員,那如何調(diào)用父類的成員呢?
這里就要用到super關(guān)鍵字了,super關(guān)鍵字就是用來在子類中調(diào)用父類的成員的,當(dāng)子類和父類有相同的成員時(shí),就使用super關(guān)鍵字區(qū)分。
super關(guān)鍵字用于子類調(diào)用父類成員變量:
下面代碼可以發(fā)現(xiàn)當(dāng)子類和父類都有同一個(gè)成員變量a時(shí),就可以通過super關(guān)鍵字來調(diào)用父類的成員變量a,當(dāng)然this既可以調(diào)用子類里面的成員變量,又可以調(diào)用父類的成員變量,只有子類和父類有相同名字成員變量時(shí),才用super加以區(qū)分。
public class Base {
int a;
int b;
}
public class Derived extends Base {
int a;
int c;
public void test1() {
this.a = 10;//this調(diào)用的父類的成員變量。
super.a = 20;//super調(diào)用的子類的成員變量。
this.b = 40;//this調(diào)用的子類的成員變量。
}
}在堆區(qū)里是這樣的:

super關(guān)鍵字用于子類調(diào)用父類成員方法:
這里和成員變量一樣,用super關(guān)鍵字來區(qū)分相同的成員方法,這里的成員方法支持方法的重載:
public class Base {
public void print() {
System.out.println("nihao");
}
public void sit() {
System.out.println("hehe");
}
}
public class Derived extends Base {
public void print() {
System.out.println("buhao");
}
public void sit(int a) {
System.out.println("hehe");
}
public void test() {
this.print();//調(diào)用子類的方法
super.print();//調(diào)用父類的方法
//構(gòu)成重載
this.sit();//調(diào)用父類的方法
this.sit(10);//調(diào)用子類的方法
}
}super關(guān)鍵字用于調(diào)用構(gòu)造方法:
當(dāng)父類有構(gòu)造方法的時(shí)候,子類也必須有構(gòu)造方法,因?yàn)樽宇愐ㄟ^super關(guān)鍵字先調(diào)用父類的構(gòu)造方法,再設(shè)置自己的成員方法,并且通過super(父類的構(gòu)造方法名)的形式調(diào)用,并且必須放在構(gòu)造方法的第一行。所以super和this不能共用,代碼如下:
public class Base {
int a;
int b;
//父類的構(gòu)造方法
public Base(int a, int b) {
this.a = a;
this.b = b;
}
}
public class Derived extends Base {
int c;
int d;
//子類的構(gòu)造方法
public Derived(int a, int b, int c, int d) {
super(a,b);//第一行先調(diào)用父類的構(gòu)造方法
this.c = c;
this.d = d;
}
}當(dāng)子類和父類都沒有構(gòu)造方法是,Java中提供了隱藏的構(gòu)造方法在子類和父類中。
當(dāng)父類中定義了無參的 構(gòu)造方法時(shí),子類的構(gòu)造方法第一行不寫調(diào)用父類的代碼時(shí),Java也會隱藏的在第一行寫上該代碼,但如果是有參的,則需要自己書寫。
1.7 this和super
相同:
- 都不能在靜態(tài)方法中使用。
- 都是關(guān)鍵字。
- 在構(gòu)造方法中調(diào)用時(shí)必須是第一條語句。
不同:
- this是對當(dāng)前對象的成員調(diào)用,而super是對從父類中繼承下來的成員的調(diào)用。
- 在構(gòu)造方法中,this用于對本類構(gòu)造方法的訪問,而super是對父類的構(gòu)造方法的訪問。兩者不能同時(shí)存在。
1.8 繼承關(guān)系上的調(diào)用順序
我們之前說過,在初始化成員變量時(shí),在實(shí)例化一個(gè)類時(shí),調(diào)用優(yōu)先級是:靜態(tài)代碼塊>實(shí)例代碼塊>構(gòu)造方法。
而在同時(shí)調(diào)用一個(gè)繼承父類的子類和該父類時(shí),調(diào)用優(yōu)先級是:父類的靜態(tài)代碼塊優(yōu)于子類的靜態(tài)代碼塊,父類的實(shí)例和構(gòu)造優(yōu)于子類的實(shí)例和構(gòu)造,而在第二次實(shí)例化對象時(shí),靜態(tài)初始化不再調(diào)用,因?yàn)轭愔患虞d一次。
1.8 protected
我們知道protected是訪問限定符,而它修飾的變量使用的范圍是同一包下的都可以訪問,再加上不同包下的子類可以訪問,代碼如下:
這里變量d是用protected修飾的在demo2包里,而在demo3里面繼承Base,可以調(diào)用d變量。
package demo2;
public class Base {
protected int d;
}
package demo3;
import demo2.Base;
public class Derived extends Base {
public void test() {
super.d = 20;
}
}1.9 繼承方式
Java里面提供了以下繼承方式:
單繼承:就是A 繼承 B。
多層繼承:A 繼承 B ,B 繼承 C
不同類繼承同一個(gè)類,A 繼承 C,B 繼承 C;
但是不支持一個(gè)類繼承多個(gè)類:A 繼承 B, A 繼承 C;這種是錯(cuò)誤的繼承方式。
1.10 final關(guān)鍵字
finai關(guān)鍵字有三種用法:
1. 密封類 當(dāng)一個(gè)類被final修飾后,則該類不能再被其他的類繼承了。
2.修飾變量:final修飾變量后該變量就變成常量了。
3.密封成員方法:表示該方法不能被重寫。
1.11 組合
組合就是說對象之間是包含關(guān)系,它可以把類與類之間分開成獨(dú)立的個(gè)體,需要的時(shí)候相互調(diào)用,不同于繼承的思想,當(dāng)父類改變時(shí)一定會影響子類,相當(dāng)于汽車這個(gè)對象是由輪胎,發(fā)動機(jī)等對象組成的,而汽車這個(gè)類里面可以調(diào)用輪胎和發(fā)動機(jī)類里面的一些屬性和方法來使用,如果這時(shí)加入一個(gè)電池類,但是油車沒有電池,電車才有,如果是繼承關(guān)系那么油車就不能繼承Car這個(gè)類了,但如果是組合思想,電車就可以單獨(dú)調(diào)用這個(gè)電池類。
class Tire {
//輪胎類
}
class Engine {
//發(fā)動機(jī)類
}
public class Car {
public Tire tire;
public Engine engine;
}2. 多態(tài)
2.1 方法的重寫
方法的重寫是在繼承的條件下,子類對父類的方法進(jìn)行重寫,子類重寫的方法,返回值類型,方法名,參數(shù)都完全相同,并且子類的訪問修飾符的范圍要大于等于父類的訪問修飾符的范圍。
當(dāng)然參數(shù)之間也可以不用相同,參數(shù)之間構(gòu)成父子類關(guān)系也可以實(shí)現(xiàn)方法的重寫,這種叫做協(xié)變類型。
可以使用@Override來判斷該方法是否重寫,如果重寫錯(cuò)誤,還會顯示報(bào)錯(cuò)。
private修飾的方法,構(gòu)造方法,靜態(tài)方法,final修飾的方法等都能重寫。
下面代碼中,Cat繼承Animal,子類對父類的eat方法進(jìn)行了重寫。
public class Animal{
public String name;
public int age;
public Animal() {
}
public void eat() {
System.out.println("正在吃食物");
}
}
public class Cat extends Animal{
public Cat() {
super();
}
@Override
public void eat() {
System.out.println("正在吃貓糧");
}
}2.2 向上轉(zhuǎn)型
向上轉(zhuǎn)型就是父類的引用指向子類的對象。下面代碼父類Animal引用指向子類Cat對象。
Animal animal = new Cat(); animal.eat();
這時(shí)候父類的引用可以調(diào)用父類的成員,但是只能調(diào)用子類重寫父類的方法,子類的其他成員都不能調(diào)用。
常見的向上轉(zhuǎn)型的方式:
- 直接賦值
- 參數(shù)傳遞
- 返回值傳遞
2.3 動態(tài)綁定和靜態(tài)綁定
動態(tài)綁定就是,在父類的引用調(diào)用重寫的方法時(shí),編譯過程調(diào)用的是父類的方法,但在運(yùn)行時(shí)綁定的卻是子類的方法,最后運(yùn)行結(jié)果調(diào)用的是子類的方法。
這是因?yàn)樵贘ava虛擬機(jī)中存在一塊空間為方法區(qū),這里的每個(gè)方法都綁定一個(gè)地址,在運(yùn)行時(shí)父類方法綁定的地址變?yōu)樽宇惙椒ń壎ǖ牡刂?,所以調(diào)用的子類的方法。
動態(tài)綁定的條件 :
- 發(fā)生向上轉(zhuǎn)型
- 存在方法的重寫
- 父類引用調(diào)用了重寫的方法
靜態(tài)綁定是在編譯時(shí)候綁定的,比如說方法的重載,根據(jù)參數(shù)來選擇調(diào)用的方法。
2.4 多態(tài)
多態(tài)就是同一種類型的引用調(diào)用同一種方法,該引用所指的對象不同,得到的結(jié)果也不同。
下面代碼中,父類的引用分別指向子類Bird和子類Cat,都調(diào)用eat方法,產(chǎn)生的結(jié)果也不相同,這就是多態(tài)。
public class Animal{
public String name;
public int age;
public Animal() {
}
public void eat() {
System.out.println("正在吃食物");
}
}
public class Cat extends Animal{
public Cat() {
super();
}
@Override
public void eat() {
System.out.println("正在吃貓糧");
}
}
public class Bird exteends Animal {
public Bird() {
super();
}
@Override
public void eat() {
System.out.println("正在吃鳥糧");
}
public void fly() {
System.out.println("正在飛");
}
}
public class Test {
public static void main(String[] args) {
Animal animal1 = new Cat();
animal1.eat();
Animal animal2 = new Bird();
animal2.eat();
}
}結(jié)果:

多態(tài)產(chǎn)生的條件:
- 子類對父類的方法進(jìn)行重寫
- 繼承的條件下
- 通過父類的引用調(diào)用子類的重寫的方法。
2.5 向下轉(zhuǎn)型
向下轉(zhuǎn)型就是說將父類引用的類型強(qiáng)制類型轉(zhuǎn)換為子類的類型。
Animal animal = new Cat();
Cat cat = (Cat)animal;向下轉(zhuǎn)型后,就可以調(diào)用子類的成員。
但是如果父類引用所指的對象的類型,與強(qiáng)制類型轉(zhuǎn)換的類型不一樣就會編譯報(bào)錯(cuò),產(chǎn)生ClassCastExceprion(類型轉(zhuǎn)換異常),所以向上轉(zhuǎn)型也不太安全,所以不經(jīng)常使用。
為了解決這種安全性,Java引入了關(guān)鍵字 instanceof 用來判斷強(qiáng)制類型轉(zhuǎn)換是否安全。
Animal animal = new Cat();
if(animal instanceof Cat) {
Cat cat = (Cat)animal;
}else {
System.out.println("不安全");
}2.6 圈復(fù)雜度
圈復(fù)雜度是用來描述代碼的復(fù)雜程度的,一般跟條件語句和循環(huán)語句的數(shù)量有關(guān),而多態(tài)就可以很好的解決圈復(fù)雜度過大的問題。
假設(shè)要打印形狀,就可以利用多態(tài)來實(shí)現(xiàn):
public class Shape {
public Shape() {
}
public void print() {
System.out.println("畫一個(gè)圖案");
}
}
public class Rect extends Shape {
public Rect() {
super();
}
public void print() {
System.out.println("畫一個(gè)矩形");
}
}
public class Cycle extends Shape {
public Cycle() {
super();
}
public void print() {
System.out.println("畫一個(gè)圓形");
}
}
public class Test {
public static void drawShapes(Shape shape) {
shape.print();
}
public static void main(String[] args) {
Rect rect = new Rect();
Cycle cycle = new Cycle();
Shape[] shapes = {rect,cycle,rect,cycle};
for(Shape shape : shapes) {
drawShapes(shape);
}
}
}打印結(jié)果如下:

利用多態(tài)實(shí)現(xiàn)的代碼的圈復(fù)雜度相對來說就很低。
注意:盡量不要在構(gòu)造方法中調(diào)用重寫的方法,構(gòu)造方法也支持動態(tài)綁定。減少使用實(shí)例化的方法。否則可能會出現(xiàn)問題??梢允褂胒inlal和private修飾的方法,它們不支持方法的重寫。
到此這篇關(guān)于Java 繼承和多態(tài)的作用及好處的文章就介紹到這了,更多相關(guān)Java 繼承和多態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot連接PostgreSQL+MybatisPlus入門案例(代碼詳解)
這篇文章主要介紹了SpringBoot連接PostgreSQL+MybatisPlus入門案例,本文通過實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-07-07
SpringBoot Admin2.0 集成Arthas的實(shí)現(xiàn)步驟
這篇文章主要介紹了SpringBoot Admin2.0 集成Arthas的實(shí)現(xiàn)步驟,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下2021-04-04
SpringBoot異步調(diào)用方法實(shí)現(xiàn)場景代碼實(shí)例
這篇文章主要介紹了SpringBoot異步調(diào)用方法實(shí)現(xiàn)場景代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Java基于Javafaker生成測試數(shù)據(jù)
這篇文章主要介紹了Java基于Javafaker生成測試數(shù)據(jù)的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12
Java復(fù)制一個(gè)對象并且不想復(fù)制其中的空值屬性問題
這篇文章主要介紹了Java復(fù)制一個(gè)對象并且不想復(fù)制其中的空值屬性問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Java動態(tài)數(shù)組的實(shí)現(xiàn)過程
本文介紹了如何實(shí)現(xiàn)一個(gè)簡單的動態(tài)數(shù)組,包括基礎(chǔ)結(jié)構(gòu)設(shè)計(jì)、核心功能實(shí)現(xiàn)、性能分析、實(shí)現(xiàn)特點(diǎn)以及改進(jìn)建議,通過這個(gè)實(shí)現(xiàn),我們能夠更好地理解動態(tài)數(shù)組的工作原理和核心操作2026-01-01
java?9大性能優(yōu)化經(jīng)驗(yàn)總結(jié)
這篇文章主要介紹了java?9大性能優(yōu)化經(jīng)驗(yàn)總結(jié),包括:Java代碼優(yōu)化,數(shù)據(jù)庫優(yōu)化,分布式緩存,異步化,Web前段,搜索引擎優(yōu)化等需要的朋友可以參考下2023-02-02

