首頁 > 軟體

Java由淺入深刨析繼承

2022-03-02 19:02:41

茫茫人海千千萬萬,感謝這一秒你看到這裡。希望我的面試題系列能對你的有所幫助!共勉!

願你在未來的日子,保持熱愛,奔赴山海!

Java基礎知識(繼承)

繼承

繼承的介紹

繼承是java物件導向程式設計技術的一塊基石,因為它允許建立分等級層次的類。描述的是事物之間的所屬關係,這種關係是:is-a 的關係。

繼承:就是子類繼承父類別的屬性和行為,使得子類物件(範例)可以直接具有與父類別相同的屬性、相同的行為。子類可以直接存取父類別中的非私有的屬性和行為。

生活中的繼承

兔子和長頸鹿屬於食草動物類,老虎和獅子屬於食肉動物類。而食草動物和食肉動物又是屬於動物類。

那是不是兔子、長頸鹿、老虎、獅子都屬於動物類呢?答案是沒錯滴!雖然食草動物和食肉動物都是屬於動物,但是兩者的屬性和行為上有差別,所以子類會具有父類別的一般特性也會具有自身的特性。我們就可以再多個類中存在相同屬性和行為時,我們可以將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只要繼承那一個類即可。

繼承的好處

  • 提高程式碼的複用性(減少程式碼冗餘,相同程式碼重複利用)。
  • 使類與類之間產生了關係。
  • 子類擁有父類別非 private 的屬性、方法。
  • 子類可以擁有自己的屬性和方法,即子類可以對父類別進行擴充套件。
  • 子類可以用自己的方式實現父類別的方法。
  • 提高了類之間的耦合性(繼承的缺點,耦合度高就會造成程式碼之間的聯絡越緊密,程式碼獨立性越差)。
  • Java 的繼承是單繼承,但是可以多重繼承,單繼承就是一個子類只能繼承一個父類別,多重繼承就是,例如 B 類繼承 A 類,C 類繼承 B 類,所以按照關係就是 B 類是 C 類的父類別,A 類是 B 類的父類別,這是 Java 繼承區別於 C++ 繼承的一個特性。

繼承的格式

在Java當中會通過extends關鍵字可以申明一個類是從另外一個類繼承而來的,一般形式如下:

class 父類別 {
}

class 子類 extends 父類別 {
}

需要注意一點: Java 不支援多繼承,但支援多重繼承。就如下:

class A {
}

class B extends A { (對的)
}

class C extends A, B { (錯的)
}

class C extends B { (對的)
}

頂層父類別是Object類。所有的類預設繼承Object,作為父類別。

繼承的demo

編寫一個父類別極其對應的子類資訊

結構如下:

程式碼如下:

父類別Person:

package com.nz.pojo;

public class Person {
    private String name ;
    private int age ;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

子類Student沒有額外的屬性和方法:

package com.nz.pojo;

/**
 * 繼承了Person特有的name, age,
 * 沒有額外的獨有屬性和方法
 */
public class Student extends Person{
}

子類Teacher多了一個工資的屬性和獨有的教書方法:

package com.nz.pojo;

/**
 * 繼承了Person特有的name, age,
 * 多了自己獨有的工資屬性還有獨有的教書方法
 */
public class Teacher extends Person{

    // 工資
    private double salary ;

    // 特有方法
    public void teach(){
        System.out.println("老師在認真教書!");
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

編寫測試程式碼:

package com.nz;

import com.nz.pojo.Student;
import com.nz.pojo.Teacher;

public class InheritDemo {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.setName("最愛吃魚罐頭");
        teacher.setAge(18);
        teacher.setSalary(1999.99);
        System.out.println(teacher.getName());
        System.out.println(teacher.getAge());
        System.out.println(teacher.getSalary());
        teacher.teach();

        Student student = new Student();
        student.setName("罐頭");
        student.setAge(12);
        //student.setSalary(1999.99); // student沒有工資屬性,報錯!
        System.out.println(student.getName());
        System.out.println(student.getAge());
    }
}

結果如下:

最愛吃魚罐頭
18
1999.99
老師在認真教書!
罐頭
12

從結果來看,子類繼承父類別,就可以直接得到父類別的成員變數和方法。而子類可以編寫一些特有的屬性和方法,但是是否可以繼承所有成分呢?

子類不能繼承的內容

並不是父類別的所有內容都可以給子類繼承的:

super 與 this 關鍵字

這裡先將這兩個關鍵字,super和this在繼承關係中,運用比較頻繁。

  • super關鍵字:我們可以通過super關鍵字來實現對父類別成員的存取,用來參照當前物件的父類別。
  • this關鍵字:指向自己本類的參照。

super和this完整的用法如下:

this.成員變數 -- 本類的
super.成員變數 -- 父類別的

this.成員方法名() -- 本類的
super.成員方法名() -- 父類別的

  • 具體演示,建立測試InheritDemo2:
package com.nz;

public class InheritDemo2 {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.eat();
        Cat cat = new Cat();
        cat.eatFish();
    }
}

class Animal {
    void eat() {
        System.out.println("animal : eat");
    }
}

class Cat extends Animal {
    void eat() {
        System.out.println("cat : eat");
    }
    void eatFish() {
        this.eat();   // this 呼叫自己的方法
        super.eat();  // super 呼叫父類別方法
    }
}

呼叫結果如下:

animal : eat
cat : eat
animal : eat

  • 注意:

子類的每個構造方法中均有預設的super(),呼叫父類別的空參構造。手動呼叫父類別構造會覆蓋預設的super()。

super() 和 this() 都必須是在構造方法的第一行,所以不能同時出現。

構造器不能被繼承

  • 子類不能繼承父類別的構造器(構造方法或者建構函式),它只是呼叫(隱式或顯式)。因為子類有自己的構造器。值得注意的是子類可以繼承父類別的私有成員(成員變數,方法),只是子類無法直接存取而已,可以通過getter/setter方法存取父類別的private成員變數。
  • 如果父類別的構造器帶有引數,則必須在子類的構造器中顯式地通過super 關鍵字呼叫父類別的構造器並配以適當的參數列。
  • 如果父類別構造器沒有引數,則在子類的構造器中不需要使用 super 關鍵字呼叫父類別構造器,系統會自動呼叫父類別的無參構造器。
  • 演示過程:
package com.nz;

public class InheritDemo3  {
    public static void main(String[] args) {
        System.out.println("------Teacher 類繼承------");
        Teacher teacher = new Teacher();
        Teacher teacher2 = new Teacher("張三");
        System.out.println("------Student 類繼承------");
        Student student = new Student();
        Student student2 = new Student("張三三");
    }
}
// 父類別
class Person {
    private String name;
    Person(){
        System.out.println("呼叫了父類別的無參構造器: Person()");
    }
    Person(String name) {
        System.out.println("呼叫了父類別的帶參構造器: Person(String name)");
        this.name = name;
    }
}
// Teacher子類繼承Person
class Teacher extends Person{
    private String name;
    Teacher(){
        // 自動呼叫父類別的無引數構造器 因為會有預設super();
        System.out.println("Teacher");
    }

    public Teacher(String name){
        super("最愛吃魚罐頭");  // 呼叫父類別中帶有引數的構造器
        System.out.println("Teacher(String name):"+name);
        this.name = name;
    }
}
// Student子類繼承Person
class Student extends Person{
    private String name;

    Student(){
        super("heihei");  // 呼叫父類別中帶有引數的構造器
        System.out.println("SubClass2");
    }

    public Student(String name){ // 自動呼叫父類別的無引數構造器
        System.out.println("Student(String name):"+name);
        this.name = name;
    }
}

結果如下:

------Teacher 類繼承------
呼叫了父類別的無參構造器: Person()
Teacher
呼叫了父類別的帶參構造器: Person(String name)
Teacher(String name):張三
------Student 類繼承------
呼叫了父類別的帶參構造器: Person(String name)
SubClass2
呼叫了父類別的無參構造器: Person()
Student(String name):張三三

final修飾的類不能被繼承

final 關鍵字主要用在三個地方:變數、方法、類。

  • 修飾類:表示該類不能被繼承;
  • 修飾方法:表示方法不能被重寫;
  • 修飾變數:表示變數只能一次賦值以後值不能被修改(常數)。

final 的特點:

  • 對於一個 final 變數,如果是基本資料型別的變數,則其數值一旦在初始 化之後便不能更改;如果是參照型別的變數,則在對其初始化之後便不 能再讓其指向另一個物件。
  • 當用 final 修飾一個類時,表明這個類不能被繼承。final 類中的所有成員 方法都會被隱式地指定為 final 方法。
  • 使用 final 方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承 類修改它的含義;第二個原因是效率。在早期的 Java 實現版本中,會將 final 方法轉為內嵌呼叫。但是如果方法過於龐大,可能看不到內嵌呼叫 帶來的任何效能提升(現在的 Java 版本已經不需要使用 final方法進行這些優化了)。類中所有的 private 方法都隱式地指定為 final。

我們測試下修飾類後到底能不能繼承:

package com.nz;

public class InheritDemo4 {
}

// 父類別
final class Fu {
    private String name;
}

//class Zi extends Fu{ // Cannot inherit from final 'com.nz.Fu' 會顯示沒辦法繼承Fu
//}

結果:可以看出來在被final修飾的Fu類沒辦法繼承,而且在編譯期間就會報錯了,沒辦法通過執行。

方法重寫

介紹

子類中出現與父類別一模一樣的方法時(返回值型別,方法名和參數列都相同),會出現覆蓋效果,也稱為重寫或者複寫。宣告不變,重新實現。

使用場景與案例

發生在子父類別之間的關係。 子類繼承了父類別的方法,但是子類覺得父類別的這方法不足以滿足自己的需求,子類重新寫了一個與父類別同名的方法,以便覆蓋父類別的該方法。

寫個測試案例:

package com.nz;

public class InheritDemo5 {

    public static void main(String[] args) {
        // 建立子類物件
        Cat lanMao = new Cat();
        // 呼叫父類別繼承而來的方法
        lanMao.run();
        // 呼叫子類重寫的方法
        lanMao.sing();

    }
}

class Animal{
    public void sing(){
        System.out.println("動物都可以唱歌!");
    }
    public void run(){
        System.out.println("動物都可以跑!");
    }
}

class Cat extends Animal {
    public void sing(){
        System.out.println("我們一起學貓叫,一起喵喵喵!讓我們一起撒個嬌");
    }
}

執行結果:

動物都可以跑!
我們一起學貓叫,一起喵喵喵!讓我們一起撒個嬌

可以看出,藍貓呼叫了重寫後的sing方法。

@Override重寫註解

  • @Override:註解,重寫註解校驗!
  • 這個註解標記的方法,就說明這個方法必須是重寫父類別的方法,否則編譯階段報錯。
  • 建議重寫都加上這個註解,一方面可以提高程式碼的可讀性,一方面可以防止重寫出錯!

加上後的子類程式碼形式如下:

class Cat extends Animal {
    // 宣告不變,重新實現
    // 方法名稱與父類別全部一樣,只是方法體中的功能重寫了!
    @Override
    public void sing(){
        System.out.println("我們一起學貓叫,一起喵喵喵!讓我們一起撒個嬌");
    }
}

注意事項

  • 方法重寫是發生在子父類別之間的關係。
  • 子類方法覆蓋父類別方法,必須要保證許可權大於等於父類別許可權。
  • 子類方法覆蓋父類別方法,返回值型別、函數名和參數列都要一模一樣。、

完結

相信各位看官看到這裡,都對Java繼承有了一定的瞭解吧,繼承在Java的特性裡還是佔據比較大得多作用,他還有很多特點:

  • 高程式碼的複用性(減少程式碼冗餘,相同程式碼重複利用)。
  • 使類與類之間產生了關係。
  • 子類擁有父類別非 private 的屬性、方法。
  • 子類可以擁有自己的屬性和方法,即子類可以對父類別進行擴充套件,可以用自己的方式實現父類別的方法。
  • 提高了類之間的耦合性(繼承的缺點,耦合度高就會造成程式碼之間的聯絡越緊密,程式碼獨立性越差)。

讓我們也一起加油吧!本人不才,如有什麼缺漏、錯誤的地方,也歡迎各位人才大佬評論中批評指正!

學到這裡,今天的世界打烊了,晚安!雖然這篇文章完結了,但是我還在,永不完結。我會努力保持寫文章。來日方長,何懼車遙馬慢!

感謝各位看到這裡!願你韶華不負,青春無悔!

到此這篇關於Java由淺入深刨析繼承的文章就介紹到這了,更多相關Java 繼承內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com