5-Java物件導向-繼承(下)

kboypkb發表於2021-09-09

前面我們學習了繼承的概念和特點;繼承的程式碼實現;方法重寫;訪問修飾符的分類及作用;super關鍵字的使用;繼承的初始化順序

課程簡介:

認識繼承體系的老祖宗: Object類

當我們不希望某些類被繼承,某些方法被重寫,某些資料被修訂時,final關鍵字登場。

註解的簡介

Object類介紹

Object類時所有類的父類

class Dog extends Animal{
  
}

一個類沒有使用extends關鍵字明確標識繼承關係,則預設繼承Object類(包括陣列);Java中的每個類都可以使用Object中定義的方法;

前往oracle官網檢視官方的api文件

Object類是存放在java.lang這個包裡的。這個包裡我們之前用到的System String Math等類都在這個包裡。這個包是系統預設會為我們直接載入的。

equals(Object obj)
Indicates whether some other object is "equal to" this one.

兩個物件是否指向同一塊記憶體。object中的equals和雙等於是同一個效果,因此經常要被我們重寫。

equals(Object anObject)
Compares this string to the specified object.

字串中重寫了equals方法,實現了字串內容的比較。

修改我們的Animal雙參建構函式,讓其將傳入的引數賦給成員變數。

    public Animal(String name,int month)
    {        this.setName(name);        this.setMonth(month);
        System.out.println("我是父類的有參構造方法");
    }
package cn.mtianyan.inherit;public class TestThree {    public static void main(String[] args) {
        Animal one = new Animal("花花",10);
        Animal two = new Animal("花花",10);

        System.out.println(one.equals(two));
    }
}

圖片描述

可以看到即使Animal沒有extends任何類,它也能呼叫Object類的方法,equals();而equals方法沒有被重寫的時候,就是雙等於,是看兩個物件是否指向同一塊記憶體。

        System.out.println(one == two);

圖片描述

可以看到,Object類中的equals和==是一樣的。equals:繼承Object中的equals方法時,比較的是兩個引用是否指向同一個物件(記憶體)

        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1.equals(str2));
        System.out.println(str1 == str2);

執行結果:

圖片描述

String型別重寫了Object類的equals方法。

Object類介紹中

        Animal one = new Animal("花花",10);
        Animal two = new Animal("花花",10);

假如我們的目的是判斷這兩個物件的值是否相同,該怎麼辦呢?

我們需要在Animal類中重寫繼承自Object類中的equals方法

    // 重寫Object中的equals方法
    public boolean equals(Object obj){        if(obj == null){            return false;
        }
        Animal temp = (Animal) obj;        if (this.getName().equals(temp.getName()) && (this.getMonth()==temp.getMonth())){            return true;
        }else {            return false;
        }
    }

進行強制轉換。有可能會出現潛在的異常

        System.out.println(one.equals(two));
        System.out.println(one == two);

圖片描述

    // 過載equals方法
    public boolean equals(Animal obj){        if(obj == null){            return false;
        }        if (this.getName().equals(obj.getName()) && (this.getMonth()==obj.getMonth())){            return true;
        }else {            return false;
        }
    }

equals測試: 1. 繼承Object中的equals方法時,比較的是兩個引用是否指向同一個物件;2. 子類可以透過重寫equals方法的形式,改變比較的內容。這裡equals中的引數為空的判斷也是很重要的,否則可能因為傳入的值為空,造成程式的異常。

由於透過Animal型別引數過載了equals()所以程式碼執行時自動定位型別匹配方法

Object類介紹(下)

Object類中的toString方法是列印出類的字串形式(類名+@+hash值)。

語句中輸出物件名時,預設呼叫toString方法。

        System.out.println(one.toString());
        System.out.println(one);

執行結果:

圖片描述

可以看到呼叫toString和直接列印一個類是一樣的。輸出物件名時,預設會直接呼叫類中的toString方法。

継承Object中的toString方法吋,輸出対象的字串表示形式:型別資訊+@+地址資訊

toString方法也經常被我們重寫,String類就將這個進行了重寫。

        System.out.println(str1.toString());
        System.out.println(str1);

子類可以透過重寫equals方法的形式,改變輸出的內容以及表現形式

圖片描述

可以看到str1就本身已經相當於呼叫了toString方法了,如果再次呼叫就會提示重複呼叫。下面我們重寫Animal中的toString方法。

    public String toString() {        return "暱稱: " +this.getName()+";年齡: "+this.getMonth();

    }
        System.out.println("================");
        System.out.println(one.toString());
        System.out.println(one);

圖片描述

養成良好的查閱文件的習慣。

final關鍵字的使用

透過繼承關係大大提高了複用性和靈活性,但是有時候我們不希望這個方法被繼承,或被重寫,值不想它被修改。

public final class Animal // final public class Animal

表示Animal這個類是最終的,是不允許有子類的。

圖片描述

public final class Stringextends Objectimplements Serializable, Comparable<String>, CharSequence
public final class Systemextends Object

java.lang下的我們用過的包,可以看到String和System都是final修飾的。

一個方法不希望被子類重寫,要在返回值之前加final關鍵字

    // 吃東西
    public final void eat(){
        System.out.println(this.getName()+"在吃東西");
    }

圖片描述

不能被重寫,但是子類仍然是可以呼叫這個方法的(不過用的是父類的實現)

final class: 該類沒有子類public final class finalpublic class

final方法: 該方法不允許被子類重寫,但是可以正常被子類繼承使用

final還可以修飾變數。

    public final void eat(){        int temp =10; // 方法內的區域性變數
        System.out.println(this.getName()+"在吃東西");
    }

方法內的區域性變數作用範圍,從該行開始到所在大括號結束。類的成員屬性的作用範圍取決於它前面的訪問修飾符

圖片描述

可以看到final關鍵字修飾過後的變數temp是不允許被再次修改的。

final 修飾的區域性變數,不需要在定義的同時必須賦值,只要在使用之前賦一次值就可以了。

        final int temp; // 方法內的區域性變數
        temp = 12;
    public final int temp =150; // 成員屬性
    public Animal(){
        temp = 20; // 報錯
        System.out.println("我是父類的無參構造方法");
    }

圖片描述

被final修飾的成員屬性如果在定義時沒有被賦值,只有兩個地方可以進行賦值: 1. 構造方法;2. 構造程式碼塊

類中成員屬性:賦值過程: 1. 定義直接初始化; 2. 構造方法; 3. 構造程式碼塊; 三者只可選其一

    public Animal(){
        temp = 10;
        System.out.println("我是父類的無參構造方法");
    }    public Animal(String name,int month)
    {
        temp = 20;        this.setName(name);        this.setMonth(month);
        System.out.println("我是父類的有參構造方法");
    }

注意: 有多個構造方法時,final關鍵字修飾的成員變數需要在多個構造方法內都要進行賦值, 不同構造方法可以賦值不同

final 關鍵字使用(下)

java 資料型別分為基本資料型別 和 引用資料型別

圖片描述

基本型別的資料是可以進行直接賦值的int temp=15;,引用型別的資料需要透過例項化來構造物件Animal one= new Animal();

對於引用資料型別被final修飾後,引用地址是否可以發生改變?屬性值是否可以發生改變呢?

        final Animal animal = new Animal("花花",10);
        animal = new Animal(); // 報錯

圖片描述

引用地址是不可以發生變化的,但是物件的屬性值是允許被重新修改的。

        final Animal animal = new Animal("花花",10);
        animal.month = 12;
        animal.name = "哈哈";
    public static final int temp; // public final static int temp
    
    static {
        temp = 12;
        System.out.println("我是父類的靜態程式碼塊");
    }

對於我們的變數而言,如果我們既不希望它在程式中不被修改,同時希望它是作為全域性變數來存在的。這種情況下使用static和final同時修飾。此時分步賦值應該在靜態程式碼塊中進行。

final:

修飾類表示不允許被繼承; 修飾方法表示不允許被子類重寫; final修飾的方法可以被繼承;不能修飾構造方法

final public Animal() // 報錯

圖片描述

只有public,protected,private可以放在構造方法前面,當然什麼都不加的預設情況也是不會報錯的

Animal(){}

修飾變數表示不允許修改:

方法內部的區域性變數 -> 在使用之前被初始化賦值即可

類中成員變數 -> 只能在定義時、構造方法、構造程式碼塊中進行

基本資料型別的變數 -> 初始賦值之後不能更改;引用型別的變數 -> 初始化之後不能再指向另一個物件,但物件的內容是可變的

可配合static使用: 可以修飾方法和變數。通常應用於: 修飾配置資訊(這類只需要載入一次,不需要後期改變的值),

public static final String MYBLOG = ""

使用final修飾可以提高效能,但會降低可擴充套件性

package cn.mtianyan.help;public class Animal {    public String name;    public int month;    public Animal() {
    }    public void eat(){
        System.out.println(this.name+"在吃東西");
    }    public void help(final Animal temp){//        temp = new Animal(); // 出錯
        temp.name = "牛牛";
        temp.eat();
    }
}
package cn.mtianyan.help;public class Test {    public static void main(String[] args) {
        Animal one = new Animal();
        one.name = "花花";
        Animal two = new Animal();
        two.name = "凡凡";
        one.help(two);
    }
}

出錯在傳入的引數是final修飾的物件,不能重新指向其他物件,但是其內容是可以改變的。

執行結果:

圖片描述

註解介紹

之前我們介紹了子類的方法重寫需要的條件

方法重寫: 1. 有繼承關係的子類中;2. 方法名相同,引數列表相同(引數順序,個數,型別),方法返回值相同;3. 訪問修飾符,訪問範圍需要大於等於父類的訪問範圍;4. 與方法的引數名無關

快速生成方法重寫,並對於是否符合條件進行有效的驗證



快捷鍵 ->

圖片描述

點選想要重寫的方法會自動的為你產生:

    @Override
    public void setMonth(int month) {        super.setMonth(month); // 呼叫父類的實現方法,一般都會註釋掉
    }

這個override刪除了也沒有問題,但是你加在其他不是重寫父類方法的就會報錯。

圖片描述

稱之為註解: JDK1.5版本引入的一個特性, 可以宣告在包、類、屬性、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明、註釋。

註解相當於一種標記,為這個程式新增註解就相當於是打上了某種標記。為方法加上override的註解,就相當於為這個方法打上標籤,告訴大家這是重寫父類的一個方法。

圖片描述

按照執行機制分: 原始碼註解;編譯時註解;執行時註解

原始碼註解: 註解只在原始碼中存在,編譯成.class檔案就不存在了。

編譯時註解: 註解在原始碼和.class檔案中都存在。

執行時註解: 在執行階段還起作用,甚至會影響執行邏輯的註解。比如spring框架,@Autowired依賴注入的這種註解,它實現的就是在程式執行的過程當中自動的將外部傳入的資訊載入進去,它就是一種可以影響程式執行邏輯的執行時註解。

圖片描述

按照來源來分: 來自JDK的註解; 來自第三方的註解; 我們自己定義的註解

元註解: 對註解進行註解。

    // 父類Animal中
    public Animal create(){        return new Animal();
    }    // 子類中重寫 可以返回子類型別,但是不可以返回Object類。向下相容
    @Override
    public Dog create() {        return new Dog();
    }

上述程式碼是不會報錯,可以正常執行的。這說明我們之前要求的重寫和父類返回值相同時不嚴謹的。方法返回值可以從父類型別變為子類型別

這裡的相容問題多型部分我們還會講的。

程式設計練習

請使用物件導向的思想,實現楊梅和仙人蕉的資訊描述。

程式參考執行效果圖如下:

圖片描述

思路分析:

  1. 根據楊梅和香蕉的共性,抽取父類水果(Fruits) 私有屬性:水果的形狀(shape)和口感(taste) 方法:

  • 帶參建構函式(引數為shape和taste)

  • 建立無參無返回值得方法eat(描述內容為:水果可供人們食用!)

  • 重寫equals方法,比較兩個物件是否相等(比較shape,taste)

package cn.mtianyan.fruits;public class Fruits {    private String shape;    private String taste;    public Fruits(String shape, String taste) {        this.shape = shape;        this.taste = taste;
    }    public void eat(){
        System.out.println("水果可供人們食用!");
    }    public String getShape() {        return shape;
    }    public void setShape(String shape) {        this.shape = shape;
    }    public String getTaste() {        return taste;
    }    public void setTaste(String taste) {        this.taste = taste;
    }    public Fruits() {
    }    @Override
    public boolean equals(Object object){        if (object == null){            return false;
        }
        Fruits temp = (Fruits) object;        if (this.getShape().equals(temp.getShape()) && this.getTaste().equals(temp.getTaste())){            return true;
        }else {            return false;
        }
    }
}



作者:天涯明月笙
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1916/viewspace-2818698/,如需轉載,請註明出處,否則將追究法律責任。

相關文章