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 修飾的.
堅持兩字,簡單,輕便,但是真正的執行起來確實需要很長很長時間.當你把堅持兩字當做你要走的路,那麼你總會成功.