1. 程式人生 > >繼承&多型&重寫&過載 理解小結

繼承&多型&重寫&過載 理解小結

java面向物件裡邊包含了:繼承 多型 過載 重寫
下面是查詢到的有用資料和自己的理解的結合
一、繼承
1、繼承:繼承顧名思義,就是子類繼承了父類的特徵(java裡邊的特徵是方法)。可以使用 extends 和 implements 這兩個關鍵字來實現繼承。
區別:extends是繼承父類,只要那個類不是宣告為final或者那個類定義為abstract的就能繼承,JAVA中不支援多重繼承,但是可以用介面來實現,extends只能繼承一個類,implements可以實現多個介面.注:extend可以繼承一個介面,但仍是一個介面,也需要implements之後才可用
e.g. class A extends B implements C,D,E
2、作用:先明確,大量重複的程式碼在程式設計中是十分不好的。如果我們想編寫兩個類,牛和馬,那麼他們共同的方法都有吃草
(1)extends 關鍵字例子

public class horse{
    private String name;
    public horse(String name){
        this.name = name;
    }
    public void eatGrass(){}
    public void sleep(){}
}

public class cow{
    private String name;
    public cow(String name){
        this.name = name;
    }
    public void eatGrass(){}
    public
void sleep(){} }

除了型別不一樣,其他都一樣,顯得累贅。如果用了繼承,讓程式碼更簡潔,變成總-分關係.兩個方法直接繼承了,修改以後:

public class grassAnimal{
    private String name;
    public grassAnimal(String name){
        this.name = name;
    }
    public void eatGrass(){}
    public void sleep(){}
}

public horse extends grassAnimal{
    public
horse(String name){ super(name); } } public cow extends grassAnimal{ public horse(String name){ super(name); } }

注1:如何呼叫horse的sleep方法

grassAnimal a = new horse("horse");//向上轉型
a.sleep();

但是如果horse有一個方法run,下面這樣的寫法是錯的,因為編譯的時候是使用grassAnimal的run方法來檢查,如何能通過編譯,執行的時候,才採用horse的run方法

grassAnimal a = new horse("horse");
a.run();

應該改成:

horse a = new horse("horse");
a.sleep();

下面這個例子更加清楚:

這裡寫圖片描述

注2:一個例子

public class Wine {
    public void fun1(){
        System.out.println("Wine 的Fun.....");
        fun2();
    }

    public void fun2(){
        System.out.println("Wine 的Fun2...");
    }
}

public class JNC extends Wine{
    /**
     * @desc 子類過載父類方法
     *        父類中不存在該方法,向上轉型後,父類是不能引用該方法的
     * @param a
     * @return void
     */
    public void fun1(String a){
        System.out.println("JNC 的 Fun1...");
        fun2();
    }

    /**
     * 子類重寫父類方法
     * 指向子類的父類引用呼叫fun2時,必定是呼叫該方法
     */
    public void fun2(){
        System.out.println("JNC 的Fun2...");
    }
}

public class Test {
    public static void main(String[] args) {
        Wine a = new JNC();
        a.fun1();
    }
}

結果

Wine 的Fun.....
JNC 的Fun2...

只是因為例項化的JNC並沒有fun1()無引數的方法,所以只能呼叫Wine的

(2)implement
如果使用interface來定義grassAnimal

public interface grassAnimal {
    public void eatGrass();
    public void sleep();
}

public class cow implements grassAnimal {
}
public class horse implements grassAnimal {
}

3、構造器
(1)解釋:兩種情況:父類的構造器沒有引數,系統自動呼叫父類的無參構造器;父類的構造器有引數,需要通過super關鍵字才能呼叫父類的構造器(具體看例子分析)
(2)舉例:

class SuperClass {
  private int n;
  SuperClass(){
    System.out.println("SuperClass()");
  }
  SuperClass(int n) {
    System.out.println("SuperClass(int n)");
    this.n = n;
  }
}
class SubClass extends SuperClass{
  private int n;
  SubClass(){
    super(300);
    System.out.println("SubClass");
  }  

  public SubClass(int n){
    System.out.println("SubClass(int n):"+n);
    this.n = n;
  }
}
public class TestSuperSub{
  public static void main (String args[]){
    SubClass sc = new SubClass();
    SubClass sc2 = new SubClass(200); 
  }
}

輸出結果

SuperClass(int n)
SubClass
SuperClass()
SubClass(int n):200

為什麼會有第三句的出現呢?
解釋:
1、子類不能繼承父類的構造器。
2、繼承父類的時候,構造器不寫super,這個時候,編譯器會自動地新增一個空引數的預設super構造器(父類無引數的構造器),此時如果父類中沒有空引數的預設構造器,那就會導致一個編譯錯誤。(意思是,子類的每一個方法都有super,如果有引數,那就用父類有引數的構造器,如果不寫的話,編譯器依舊會給你加上super,採用父類無引數的構造器,如果不存在這個無引數構造器,編譯出錯)
例如:subClass的第二個方法

public SubClass(int n){
    System.out.println("SubClass(int n):"+n);
    this.n = n;
  }

補全後是,如果SuperClass沒有空的構造器就編譯出錯了

public SubClass(int n){
    super();
    System.out.println("SubClass(int n):"+n);
    this.n = n;
  }

二、過載和重寫
菜鳥教程
(圖借鑑了菜鳥教程)
這裡寫圖片描述

過載:Overloading (好多種,只是引數不同)
重寫:Overriding(改變這個方法的實現方式)
這裡寫圖片描述

三、多型
1、重寫
2、介面
3、抽象方法

  • 抽象類不能被例項化,只能被其他類繼承
  • 繼承抽象類的子類必須把抽象類中的所有抽象成員都重寫(實現)(除非子類也是抽象類)
  • 抽象類就是為了重寫→多型(程式碼重用)

三種實現

引用看到的部落格的一段話:“如何區分多型和繼承”
繼承:子類繼承父類中所以的屬性和方法,但是對於private的屬相和方法,由於這個是父類的隱私,所以子類雖然是繼承了,但是沒有可以訪問這些屬性和方法的引用,所以相當於沒有繼承到。很多時候,可以理解為,沒有繼承。
多型:就是父類引用可以持有子類物件。這時候只能呼叫父類中的方法,而子類中特有方法是無法訪問的,因為這個時候(編譯時)你把他看作父類物件的原因,但是到了執行的時候,編譯器就會發現這個父類引用中原來是一個子類的對像,所以如果父類和子類中有相同的方法時,呼叫的會是子類中的方法,而不是父類的。
可以這麼說:編譯時看父類,執行時看子類。