1. 程式人生 > >Java開發知識之Java的繼承多態跟接口*

Java開發知識之Java的繼承多態跟接口*

們的 class 參數順序 程序員 數據 父類 com 應該 手機

          Java開發知識之Java的繼承多態跟接口

一丶繼承

  1.繼承的寫法

  在Java中繼承的 關鍵字是 extends 代表一個類繼承另一個類.

繼承的含義以及作用: 繼承就是基於某個父類的擴展.制定出來的一個新的子類.子類可以繼承父類原有的屬性跟方法. 也可以自己增加特有的屬性跟方法.

代碼例如下:

public class Anmail {
    public void eat() {
        System.out.println("父類吃");
    }

}
父類代碼

子類代碼:

public class Dog extends Anmail {
    
public void eat() { System.out.println("子類吃"); } }

通過上面代碼.我們可以看到.子類 Dog類. 繼承了父類. 使用了關鍵字 extends

並且子類重寫了父類的方法.

  2.子類訪問父類的方法

  上面說了子類繼承父類.那麽子類也可以調用父類的方法. 我們學過this關鍵字. this可以區分局部變量跟成員變量.也可以在構造中調用其它構造函數.

那麽我們還提供了一個關鍵字. super() super關鍵字可以訪問父類.

代碼如下:

父類代碼一樣.之帖子類代碼.

public class Dog extends Anmail {
    
public Dog() { super(); //調用父類構造 調用構造的時候必須放在最上面. super.eat();//調用父類方法. } public void eat() { System.out.println("子類吃"); } }

創建子類對象.並且輸出.

技術分享圖片

可以看到.在調用構造的時候.他會先訪問父類的構造.但因為父類構造我們並沒有輸出內容.所以沒有輸出內容,子類繼續調用父類的eat方法. eat方法我們輸出了.

就是父類吃. 所以在子類構造的時候.會調用父類構造以及父類的方法.

  super()的關鍵字用法限制. super關鍵字只能調用父類中 公共權限(public) 以及保護全選(protected)的方法

  3.重寫的概念

  子類可以重寫父類的方法. 什麽是重寫.就是子類跟父類的方法是一模一樣的. 也就是說,重寫是在子類跟父類中才會出現的. 返回值一樣. 方法名一樣. 參數一樣.

在J2SE 5.0 以上.支持了新的功能.也就是說返回值可以不一樣.但是 方法名 跟 參數必須一樣.

  JAVA 類編譯的流程. java中.創建子類的時候.會自動調用父類的構造方法進行初始化. 我們可以做個例子. 並且重寫一個方法.

public class Anmail {
    public Anmail() {
        System.out.println("父類構造方法");
    }
    public void eat() {
        System.out.println("父類吃");
    }

}

子類.

public class Dog extends Anmail {
    public  Dog() {
        
    }
    public void eat() {                //重寫父類方法
        System.out.println("子類吃");
    }
}

創建對象.

技術分享圖片

通過實例可以終結出. 1. 子類重寫了父類方法.輸出的內容是自己的"子類吃" 2.在給子類實例化的時候.會自動調用父類進行實例化操作.也就是說父類也會被初始化.

PS: 子類實例化的時候.會調用父類的無參構造進行實例化父類.但是並不會自動調用父類的有參構造.這個我們需要使用super關鍵字才可以.

二丶Object 類

  object類是一個比較特殊類的. 位於java.lang.包中. 它是所有類的父類.比如我們以前學習過字符串類. String類. String類中 我們比較兩個對象是否相等就是用.

equleas()方法. 這個就是object類中的.只不過字符串進行了重寫. 我們自定義的類也是繼承自object類.只不過是默認繼承. 所以任何類都可以重寫父類object中的方法.

在object類中 加了final類的方法是不能被重寫的. 例如 getClass() notify() notifyAll() wait()等等.

object類的方法介紹.

  1.getClass()方法

    getClass()方法會返回指定是的Class實例. 然後可以使用此時調用getName()獲得這個類的名稱.

    getClass().getName(); 也可以配合toString()方法使用.

  2.toString()方法

    toString()方法就是返回一串字符串.在object類中,就是講一個對象返回為字符串形式.實際應用中就是重寫這個字符串.返回什麽是你自定的.

  3.equals()方法;

    equals()方法就是比較.當時說過區別. 就是 == 與 equals()的區別. == 是比較地址. equals()是比較你自定的內容.也就是對象的實際內容.通常也是重寫.

比較什麽你自己定. 如果你寫了一個類有一個成員變量是 a; 我們重寫equals() 就判斷 a 跟 比較對象的a即可. 就是 a 跟 a比較.

三丶對象類型轉換.

    對象類型轉換.包括向上轉型.以及向下轉型. 通俗理解就是 強轉對應的數據類型. 但是你在強轉的時候要判斷一下是否是這個數據類型.這個就是轉型.

向上轉型以及向下轉型就是說 類我們強轉為父類. 也可以父類強轉為子類.

1.向上轉型

   子類對象賦值給父類對象稱為向上轉型. Anmail a = new Dog(): 這個就是向上轉型.

比如我們有動物對象. 跟 狗對象. 狗對象可以看做是動物對象的一個子類.

還比如 四邊形類 跟 平行四邊形類. 平行四邊形 對象可以看做是 四邊形類的一個對象.

如下圖:

  技術分享圖片

常規的繼承圖都是父類在上. 子類在下.例如上圖. 所以我們將子類看做是父類對象的時候成為向上轉型. 也就是平行四邊形對象看做是四邊形類的對象的時候.

向上轉型是具體的類像抽象的類進行的轉換.所以它總是安全的. 我們可以說平行四邊形是特殊的四邊形. 但是不能是四邊形是平行四變形.

因為代碼寫法: 四邊形 a = new 平行四變形(); 所以很多人就會說 a就是平行四邊形. 其實是錯的. a是四邊形. 我們只能說a平行四邊形是一個特殊的四邊形.

  如果在C++ 中.內存分配就是 父類占一小塊內存. 子類上半部分是父類內存.下半部分是子類特有的成員變量開辟的內存. 子類轉為父類. 就是不要子類下邊的內存了.

所以總是安全的. 我只要上面的哪塊內存.也就是父類的內存.

所以在上邊. 子類轉為父類. 父類調用方法的時候.並不會調用到子類特有成員變量.

2.向下轉型

  向下轉型就是 抽象的類轉為具體的類. 比如 動物是鳥. 動物是抽象的.不能說他是鳥.所以不和邏輯.而且會出現問題.

比如父類 Anmail a = new Dog();

    Dog b = a; 這樣是錯誤的. 我們不能這樣賦值.原因就是不能說 動物是狗.

    Dog c = (Dog)a; 這樣可以.強轉為子類型.寫法是正確的.

站在C++的角度:

  為什麽上面向下轉型是錯誤的. 原因是 a 會有一塊內存. 我們可以假定為0x20個字節大小. b是Dog也就是子類對象.他繼承了父類.有自己特有的成員方法

以及成員變量. 所以它的頭0x20個字節是父類的內存.下面多出的內存是自己了.假設是0x30個字節. 所以我們子類轉為父類(向上轉型)

其實就是把30個字節的內存轉為20個字節的內存.所以不會出問題. 但是 0x20個字節.也就是父類轉為子類. 就會出為題. 意思就是說 0x20個字節轉為0x30個字節.

首先我們並不知道是轉為0x30個字節.這樣內存訪問就會出錯了.但是如果我們強轉了.相當於就是父類在強轉為子類的時候.按照子類的內存強轉.這樣就不會有問題了.

也就是上面的我們的 Dog c c的頭0x20個字節是父類. 下面的0x10個字節就是自己特有的所以不會出錯.

3.使用關鍵字判斷對象類型

    上面我們的父類轉為子類的時候.必須加上子類的數據類型才可以強轉. 原因就是轉為子類的時候.內存會按照子類的大小進行擴大.這樣就不會出現問題了.

但是我們怎麽知道 我們的子類.是否是這個父類的子類. 所以有了運算符 instanceof()來判斷.

就是判斷 子類是否是父類的. 父類中有沒有這個子類.如果有就進行轉換.

語法:

  Myobject instance ExampleClass

1.Myobject 就是某個類的對象引用. 可以理解為是父類填寫的是父類引用.

2.ExampleClass 某各類. 可以理解為子類. 填寫類名.

例如:

  Anmail a = new Anmail();

if (a instanceof Dog)

{

    判斷父類中是否有子類Dog,如果有我就進行轉換.

    Dog d = (Dog) a;
    d.xxxx;
}

四丶方法重載.

  1.重載介紹.

  重載的含義. 重載就是可以有多個相同函數.重載的參數確定是否重載.

我們寫過有參構造跟無參構造. 方法名是一樣.不一樣的就是參數列表不同.

  重載的構成:

    1.重載的構成是方法名字一樣,

    2. 參數列表個數不同

    3. 參數類型不同

    4.參數列表順序不同

例如:

  public void eat();

  public void eat(String,int); 重載eat,參數是String,int

  public void eat(int,String); 參數順序不同.構成重載.

  public int eat(int,int); 返回值是int. 參數列表是兩個int值. 返回值也可以為void不影響重載.

PS: 方法的返回值並不會影響重載.真正影響的是參數列表. 你有兩個成員方法.方法命一樣.參數列表一樣.類型一樣.返回值不同.不能構成重載.

特殊的重載:

  Java中可以定義不定長的參數類表.

語法如下:

    返回值 方法名(參數數據類型 . . . 參數名稱) 主要是三個...

其實不定長參數.就是一個一維數組.我們可以當做數組去操作參數.

代碼如下:

  

    public void eat(int ... a) {
        for (int i = 0; i < a.length;i++)
        {
            .......
        }
    }

5.多態的概念

  在上面我們學過向上轉型.就是子類對象可以當做父類對象去使用. 其意思就是我可以當做父類對象去使用. 那麽就可以使用子類跟父類的共有的方法跟屬性了.

因為我們站在內存的角度上也說了.我們用的都是父類的內存.所以我們可以調用父類的方法.跟成員去使用. 但是多態是什麽意思.多態就是調用父類的方法的時候.

因為子類重寫了父類的方法.所以調用時會調用子類的特有方法.

例如:

  Anmail a = new Dog();

  a.eat()

輸出結果: 子類吃. 按理說應該輸出父類吃.不是說向上轉型了.我們用的都是父類內存了.子類就不該會被調用. 願意你是這樣了.如果站在C++角度來說.

我們首先會new一個子類的對象實例. 而new子類對象的時候.會先初始化父類. 在初始化自己. 為什麽說一下流程.原因是父類有虛表.也就是有一個表.保存著

自己的方法.而子類在實例化的時候.父類的虛表先初始化. 初始化完了之後.子類的再初始化.它會先把父類的虛表拷貝過來.然後覆蓋他. 這樣我們a調用方法的時候

其實調用的是子類方法.原因就是子類的虛表已經覆蓋了父類虛表.

Java中的原理. java中其實也是一樣的.只不過給你隱藏了這個步驟了.不用理解的這麽復雜.我們只要知道.向上轉型之後.調用子類跟父類共有的方法.就能實現多態.

註意: 子類重寫了父類方法.那麽調用的時候才是子類的方法.原因是子類重寫了.才會覆蓋父類.

代碼例子:

技術分享圖片

多態的用法: 多態的好處就是程序員不同定義相同的方法了.避免了相同的大量重復代碼的開發.只要實例化一個子類對象.維護這個方法即可.

再舉個例子;

我們手機. 有一代手機 二代手機 三代手機 四代手機 ...n代手機.

1代手機 只支持打電話

2代手機 可以發信息了.

3.手機 可以上網了

4.手機 可以拍照了.

此時我們只需要二代手機繼承1代手機對1代手機擴展功能. 比如增加發信息的方法. 打電話的方法進行重寫.可以打電話.也可以錄音了. 而一代手機根本不用動方法.

此時我們的二代手機就可以出手了.第三代手機同樣繼承第二代手機.增加擴展功能.重寫維護的方法. 而不用修改二代的代碼.

這樣不管我們有第幾代手機.只需要繼承上一代的類.進行擴展.以及維護即可.

6.抽象類

  抽象類就是說一個不可以被存在的類. 比如我們有動物 跟 狗. 而動物是不能被實例化對象的. 狗是一個具體的生物.我們可以實例化.

所以抽象就是指 動物. 也就是說我不能讓你被實例化.原因就是 動物泛指萬千.不能具體為一個動物.

定義抽象類的關鍵字

abstract

  使用abstract 修飾的類稱為抽象類.是不能實例化的.

  使用 abstract 修飾的方法.稱為抽象方法.是不能實現的.比如子類重寫. 修飾的方法沒有方法體.

 反過來說.如果一個類中修飾了成員方法.那麽就必須定義這個類為抽象類.

抽象類跟普通類一樣.只不過就是不能實例化. 必須要有子類繼承.如果有抽象方法.子類必須重寫.

抽象類的繼承圖:

技術分享圖片

代碼寫法,需要將我們的Anmail類寫成抽象類. 並且方法改為抽象方法

//public abstract class Anmail
abstract public   class  Anmail {

    abstract public void eat() ;
    public abstract void  play();
}

abstract 卸載權限修飾符的前邊或者後邊都可以.不影響.例如類上面加了一行註釋.我們也可以寫成上面的寫法.以及抽象方法.也是兩個不同順序來舉例子

public class Dog extends Anmail {

    @Override
    public void eat() {
        // TODO 自動生成的方法存根
        System.out.println("狗在吃東西");
    }

    @Override
    public void play() {
        // TODO 自動生成的方法存根
        System.out.println("狗在玩耍");
    }

}

使用的時候.可以使用向上轉型.使用多態的方式. 子類可以自己new自己

技術分享圖片

在C++中.也有抽象類的概念.只不過稱之為純虛類. 他的方式就是這個類中的方法定義為純虛方法(抽象方法)

void eat() = 0; 後面加上 = 0; 跟接口很類似. 一般也是接口

七丶接口的含義

  接口就是抽象類的延伸.上面我們定義了一個抽象類.如果這個抽象類是一個父類.有很多子類.但是我們可以這樣想一下. 如果子類很多.都要實現這個抽象類中的方法.

這樣就造成了代碼冗余. 我們有的類完全可以不實現抽象類中的抽象方法啊.

比如 play方法. 我們每個子類都要實現.但是有的動物就不會玩我完全可以不實現了.但是按照抽象類.我們必須實現.所以就代碼冗余了.

此時接口就出現了. 接口就是說 . 我接口中的方法都是抽象方法. 你要實現play. 就可以實現我這個接口. 如果不需要玩的話.就不用實現我這個接口

例如下圖:

技術分享圖片

可以看到我們的N邊型類.並沒有實現接口.我完全可以不用實現Draw方法.雖然我繼承了父類.

接口的定義與使用:

  1.接口使用intface關鍵字 修飾. 可以使用權限修飾符修飾

  2.接口的實現使用關鍵字 implements關鍵字

  3.接口中的方法都是抽象方法.而且都沒有方法體. 且默認加的關鍵字就是abstract 而且權限必須是public因為要被實現.其它權限修飾則不被編譯器認可.

  4.在繼承一個類同時.可以實現接口.

  5.在接口中定義的 字段(成員變量)都是默認修飾符 static + final修飾的. 也就是說一個靜態常量.

代碼如下:

  定義一個接口

public interface IAnmail {
    
    public abstract void Play(); //定義一個抽象方法
}

接口的實現.

  

public class Dog extends Anmail implements IAnmail{

    @Override
    public void eat() {
        // TODO 自動生成的方法存根
        System.out.println("狗在吃東西");
    }

    @Override
    public void Play() {            //實現了接口必須實現他的方法
        // TODO 自動生成的方法存根
        System.out.println("狗在玩耍");
    }


}

技術分享圖片

八丶總結

  1.類的繼承

    類的繼承使用 extends關鍵字

    註意的問題:

      1.不能多繼承.

      2.子類繼承父類自動用於父類的方法以及成員變量

      3.子類在構造中可以調用父類構造.(無參或者有參) 使用的是 super();關鍵字. 也可以調用父類方法 super.xxxx

  2.公共父類Object

    在Java中每個類都繼承了父類Object. Object中提供了常用的方法

  比較: equeals();

  轉為字符串表現形式 toString();

  這些方法都是可以被重寫的.除了加了 final修飾的.

  3.對象的類型轉換.

  在Java中有向上轉型跟向下轉型

    1.向上轉型: 子類轉為父類. Anmail d = new Dog(): 安全的.因為在內存角度來說.使用的部分只有父類部分內存.

    2.向下轉型: 父類轉為子類 Dog c = (Dog) d; 必須加上類型.父類內存轉成子類內存,比如給指定子類內存大小.

    3.對象類型判斷 使用 instanceof

    語法 父類引用 instance 子類類名 判斷父類是否有這個子類

    如果是我們就可以進行轉換了.

  4.類中的方法重寫與重載

    重寫:

      1.返回值相同

      2.方法名相同  

      3.參數列表相同

    滿足以上三點才能是重寫.也就是子類跟父類一模一樣才是重寫.

    重載:

       1.方法名一樣

        2.參數列表個數不同

       3.參數列表類型不同

        4.參數列表順序不同.

    滿足以上四點才能構成重載.

    特殊重載可以使用 三個點定義為可變參數 public void eat(int ...a); 此時a就是一個數組.我們可以遍歷他來獲取參數.

  5.多態

   多態就是父類可以調用子類跟父類共有的方法.比如是子類重寫父類才會有不同的結果.

  使用向上轉型

    Anmai a = new Dog(); a.eat() ; 狗在吃東西.

  內存角度來說. 就是虛表覆蓋.

  6.抽象類

    抽象類使用 abstract關鍵字進行修飾

    public abstract class 類名{}‘

    1.修飾類就是抽象類,不可以被實例化.就是不能創建對象

    2.修飾方法就是抽象方法,沒有方法體.子類繼承比如重寫.

    3.一個類中有抽象方法.則這個類必須定義為抽象類.

    

  7.接口

    接口定義的關鍵字 Intface public intfact 接口名{}

    實現接口的關鍵字 implements public class Anmail Implements 接口名

  1.接口中的方法都是抽象方法. 且默認修飾符為 public abstract 權限必須是public 否則編譯器不認可.

  2.接口中的成員變量都是默認的 static final 修飾的.

  

  

Java開發知識之Java的繼承多態跟接口*