1. 程式人生 > >小白理解——封裝繼承多型

小白理解——封裝繼承多型

                                          一、封裝

是什麼:首先是抽象,把事物抽象成一個類,其次才是封裝。對外表示為一個物件,隱藏物件的屬性和動作實現的細節,僅對外公開介面。

為什麼:對外簡化程式設計。高內聚低耦合使用者不需要了解具體的實現細節,而是要通過外部介面,一特定的訪問許可權來使用類的成員(包括成員函式和成員變數)。對內保護資料安全,使用者不可隨意修改屬性值。此外,封裝符合面向物件設計原則的第一條:單一性原則,一個類把自己該做的事情封裝起來,當內部的邏輯發生改變時,外部呼叫不用因此而修改。

怎麼做:基本要求是把所有的成員變數(物件的屬性)私有化。對每個屬性提供get和set方法。

補充:C++相比,Java的封裝更徹底(C++向後相容C,而C是面向過程的程式設計,所以C++允許把函式和變數寫在類外,而java不允許)。Java成員函式的實現是直接寫在class裡面的(不管這個成員函式的實現有多複雜),而C++是在class裡面寫簡短的函式宣告,然後在class外面寫函式具體的實現。當然C++也可以在類內實現,只是當時老師上課要求統一寫在外面。在class裡只寫宣告,在類外通過作用域符::寫實現。

師兄補充:程式設計師封裝類的好壞的評定標準:職責單一、擴充套件性、執行結果是否清晰;

簡言之,封裝就是無需暴露細節,暴露該暴露的,隱藏該隱藏的,讓任意兩者之間的耦合恰當。(

eg.買車只需要會開即可,不需要關注引擎、發動機等工作細節)

                                  二、繼承

是什麼:當兩個類具有相同的特徵(屬性,比如售票員和乘客都是“人”)和行為(方法,比如售票員和乘客都要吃飯和睡覺)時,可以將相同的部分抽取出來放到一個類中作為父類(上面的例子中,“人”就是父類),其他兩個類繼承這個父類。繼承後子類自動擁有了父類的屬性和方法,目的是實現功能的擴充套件,子類也可以複寫父類的方法,即方法的重寫(這裡穿插著多型的內容)。子類不能訪問父類中訪問許可權為private的成員變數和方法。子類可以重寫父類的方法,即命名與父類同名的成員變數。

為什麼:繼承可以重複使用程式碼,降低冗餘。程式碼重用是一點,最重要的一點是可以向上轉型(將匯出類看為它的基類的過程),這是Java面向物件最重要的特性——多型的基礎。

怎麼做:子類繼承父類所有,只是訪問受約束。通過關鍵字extends繼承。

補充:java類可以分為三類

1.類:使用class定義,沒有抽象方法

2.抽象類(不能被例項化 比如“水果”是一個抽象類,但無法例項化它,拿不出一個具體的東西):只能被繼承用abstract class定義

3.介面(對抽象類進一步的抽象,比如飛機和鳥都會飛 “非”就可以被設計成一個介面):使用interface定義,只能有抽象方法,所有的屬性都是static final來定義的。

師兄補充:簡單地說就是為了程式碼複用,為多型提供基礎。

                                    三、多型

是什麼:首先要知道方法的唯一性標識是方法名和引數(引數個數 型別 順序)。同一操作作用於不同的物件,可以有不同的解釋,產生不同的執行結果,這就是多型性。

 多型可以說是“一個介面,多種實現”或者說是父類的引用變數可以指向子類的例項,被引用物件的型別決定呼叫誰的方法。

為什麼:程式的可擴充套件性及可維護性增強。這裡我覺得可以類比於C++中的模板類來理解,針對使用者輸入引數的個數和型別來執行相應的操作。我看其他論壇上也有觀點是多型為了介面重用(以我現在的理解來看,就是子類可以複用父類的方法)。

怎麼做:首先java實現多型需要有三個條件:繼承(在多型中必須存在有繼承關係的子類和父類)、重寫(子類對父類的某些方法進行重新定義,在呼叫這些方法時就會呼叫子類的方法)、向上轉型。

具備以上的前提條件後,實現多型的方法(C++中實現多型的方法有函式重寫重寫是指子類重新定義父類虛擬函式的方法與過載指允許存在多個同名函式,而這些函式的引數列表不同)有編譯時多型(方法的過載)和執行時多型(繼承時方法的重寫)。

編譯時多型很好理解,就是編譯器在編譯的時候會根據函式不同的引數表,對同名函式的不同名稱做不同的修飾,這些同名函式就成了不同的函式。也正是因為這一點,師兄說函式過載不算多型。

執行時多型依賴於繼承、重寫和向上轉型(簡單的理解為,匯出類可以看成它的基類)。基於繼承實現多型:

public class Toilet{

    public void toilet(){

        system.out.print("廁所")

        }

    }

public class M extend Toilet{

    public void toilet(){

         system.out.print("男廁")

        }

    }

public class W extend Toilet{

    public void toilet(){

        system.out.print("女廁")
        
        }

    }
    
class A {

    public static void main(String[] args){

        Toilet mToilet = new M();    //父類引用指向子類物件

        Toilet wToilet = new W();    //父類引用指向子類物件

        mToilet.toilet();            //執行M的toilet()方法

        wToilet.toilet();            //執行W的toilet()方法 

        }

    }

    程式執行結果: 男廁女廁

    程式碼連結:https://www.jianshu.com/p/1d6219ad43a1
    程式碼來源:簡書

    基於介面實現多型:

public interface Toilet{

    public void toilet();

    }

//介面實現類

public class M implement Toilet{

     public void toilet(){ system.out.print("男廁")

    }

}

//介面實現類

public class W implement Toilet{

    public void toilet(){

        system.out.print("女廁")

        }

    }

class A{

    public void static main(String[] args){
    
        Toilet mToilet = new M(); //介面引用變數指向介面實現類物件

        Toilet wToilet = newW(); //介面引用變數指向介面實現類物件

        mToilet.toilet(); 

        wToilet.toilet();

        }

    }

程式執行結果: 男廁女廁
程式碼連結:https://www.jianshu.com/p/1d6219ad43a1
程式碼來源:簡書

很棒的例子:

class Human {



    public void fun1() {

        System.out.println("Human fun1");

        fun2();

    }

    public void fun2() {

        System.out.println("Human fun2");

    }

}

class Programmer extends Human {

 //函式的過載

    public void fun1(String name) {

        System.out.println("Programmer's fun1");

    }

 //函式的重寫

    public void fun2() {

        System.out.println("Programmer's fun2");

    }

}

public class Test {

    public static void main(String[] args) {

        Human human = new Programmer();//出現“型別不匹配時”,判斷口訣:變數多型看左邊,方法多型看右邊,靜態多型看左邊

        human.fun1();//沒有對應的fun1()函式所以向上轉型human型別,執行human中的fun1()函式。而funl()中含有fun2(),而此函式在子類中被重寫了,所以執行子類的fun2();    }

  

 /*

     * Output:

     *  Human fun1

     *  Programmer's fun2

     */

}

師兄補充:跳出多型的細節,從整體上看多型就是一種表現形式有多種結果呈現。

                                  四、結語

剛剛告訴師兄我的作業名多型繼承封裝的時候,師兄告訴我說,我說反了。我聽了之後當時還沒有意識到這樣的順序有什麼問題,但是經過我週末的總結之後,坐在工位上恍然大悟。封裝->繼承->多型這不單單是三個名詞的簡單羅列,更是一步步遞進的關係。對於面向物件的程式設計,封裝是基礎,繼承是多型的前提條件,三者邏輯順序不可顛倒。

這三個思想是設計模式的基礎,也是整個Java的基礎,值得學而時習之。