JAVA中幾個易混淆關鍵詞的理解:行爲,隱藏,組合和繼承,覆寫和重載,多形(多態)性,動態綁定,上溯造型,抽象類,接口

 什麼是行爲?行爲規範了你能對對象發出的請求。你的CLASS,也就是對象,也就是MM,你把她設計出來了,而且你很BT,只給她設計了兩個行爲:loveMe()和makeLoveWithMe()。那麼她便不可能接受其它客戶端class(某個帥哥?)的請求,如果在某個class裏,你寫成了MM.loveF4(),那麼編譯器就會出錯。
         你理所當然把MM的屬性設成美,你不希望別人來改變這個事實,那麼,你就要把這個屬性定義爲private,這樣MM便不會在第二天醒來成爲傳說中的KL。這在第一章裏標題爲:被隱藏的實施細節。一個屬性,有四種修飾符,public,private,protected,空(默認,friendly)。分別代表的意思是任何對象可以訪問,本對象內部可以訪問,本對象(class)的繼承者可以訪問,同一個包(package)的其它對象可以訪問。
         我們總是想盡可能地讓自己的代碼變的簡潔,這便需要重複運用實現碼。JAVA提供了這種方式,其途徑有兩種:組合和繼承,假設有一個類爲A,A有一個行爲doSomething(),還有一個類B,也想do something,那麼你可以在B裏產生一個方法(也即函數)doSomethingToo( A.doSomething(); )。這叫做組合。繼承是另外一種方法,你可以直接用關鍵詞extends,讓B繼承自A,那麼你不用特殊額外表達,B在外界看來也是有doSomething的能力的。那麼我們要問,我們什麼時候要組合,什麼時候用繼承呢?BRUCE講,當B is a A的時候我們用繼承。這不太好理解,我們再舉個MM的例子。MM具備喫飯(eatYou( 進食;消化))的行爲,MM衍生開來有好色的MM、愛打屁的MM等,但她們都是MM,也就是is a的關係,這個時候你要設計好色的MM的時候就用繼承,用extends。而GG不是MM,但是GG is like a MM,因爲GG也可以喫飯。這個時候我們就不用再去寫eatYou()這個方法了,直接在GG這個類裏寫eatYou( MM.eatYou(); )那麼,GG也具備了進食、消化的功能。其實我們要常常用到組合,在程序裏,用繼承的地方是比較少的。
        在繼承當中,有兩個特殊行爲需要我們關注:覆寫(overriding)和重載(overloading)。現在你只需記住:如果base class和derive class的某個方法名稱相同,引數相同,則爲覆寫(overriding),名稱相同,引數相同,則爲重載(overloading)。

        面向對象一個顯著的優點就是多形(多態)性。我本來是不想在這第一部分寫代碼的,但是似乎這個問題用語言表達不夠直觀,所以就寫了個最簡單的能說明多形性的問題。看代碼先。。。
//Love.java
class MM{
  public void toSeeGG(){
    System.out.println("xxx");
  }
}

class HaoseMM extends MM{
  public void toSeeGG(){
    System.out.println("誘惑GG!!!");
  }
}

class BenfenMM extends MM{
  public void toSeeGG(){
    System.out.println("好羞澀哦....");
  }
}

public class Love{
  public static void loveGG(MM i){
    i.toSeeGG();
  }
  public static void main(String[] args){
    MM mm1 = new HaoseMM();
    MM mm2 = new BenfenMM();
    loveGG(mm1);
    loveGG(mm2);
  }
}
該代碼運行的結果是:

誘惑GG!!!
好羞澀哦....

我們看到由MM衍生出來有好色MM(HaoseMM),本分MM(BenMM),MM們都可能會看到帥哥,但是好色MM和本分MM看帥哥的眼神是不一樣的,如程序中定義。在愛(Love)這個類裏,我們定義一個方法loveGG,我們給它傳遞一個參數,是基類(base class)MM,然後toSeeGG()。通過本文最開頭的描訴,我們知道,HaoseMM和BenMM與MM的關係是is a的關係,所以我們在用到loveGG(mm1)和loveGG(mm2)時候編譯器不會出錯。我們看到,程序自動各自執行了haoseMM和BenfenMM的toSeeGG(),而不是打印出“xxx”。這就是多形性。之所以會如此神奇,是因爲JAVA運行時環境提供了動態綁定技術。動態綁定會讓你產生的MM在JAVA運行時環境裏按照你的指示分別作出行爲。OK,我們不需要知道動態綁定是怎麼運做的,我們只要知道這是什麼,因爲我們還在第一章。上溯造型(upcasting)這個概念也在這裏被提出來了。在loveGG(MM i)這個方法裏,方法接受的是MM,可是loveGG也接受HaoseMM和BenfenMM,這個特性就叫上溯造型。

        我們在上面的程序中看到了一句沒有用的代碼,就是在MM類裏的System.out.println("xxx");。沒有人關心基類MM是怎麼看帥哥,因爲它只是一個模版,所以我們乾脆就不要這句代碼,而且我們連{}也不要了,直接改寫這個方法爲public abstract void toSeeGG();,那麼這個方法稱做抽象方法(abstract method)。基類MM對我們來說沒有實現的必要,於是我們把class MM{...}改寫爲abstract class MM{...},那麼這個類叫做抽象類(abstract class)。我們不禁要問,那麼抽象類能不能含有非抽象的函數呢。答曰:可以。但是這在實際中沒有什麼用處,唯一用到的地方是:1,main()函數,用來測試你的類;2,考試中。那麼我們還要問,子類可以不覆寫父類中抽象方法行不行?答曰:如果子類也是abstract,那麼可以,否則不行。
        
        比抽象類做的更徹底的是接口(注意,這裏的接口是JAVA中真正意義的接口,非上文一開始的接口,也既行爲)。接口被設計出來的目的就是要讓你繼承的。等………,這是第一章,只讓大家知道一些概念就行了,上面我們說的太多了。
        
        BRUCE是個徹底的NN大師,我們接下來看到他把JAVA的低效率問題說的是那麼的理所當然,以至於我們看完後情不自禁也要說: 恩,JAVA就應該這樣做,讓C++見鬼去吧。面向對象,於是所有東東都是對象,有對象便有對象的產生和毀滅。程序運行的時候,對象產生在內存中。內存分配有三種策略,分別是靜態的,棧式的(stack),和堆式的(heap)。C++使用前兩種策略,JAVA僅使用後者。兩者有什麼區別呢?喜歡尋根究底的朋友請參考本站JSP/JAVA分壇的“棧存儲(stack)和堆存儲(heap)的區別 ”,我們只要記住,後者在內存中的尋址要花更長的時間,因此JAVA的效率低。但是JAVA提供了根據heap特點產生的垃圾回收機制。垃圾回收機制完成這樣的任務,當你的對象不在使用的時候它會自動察覺並消滅。你不用再擔心發生內存泄露了。而這是每個C++程序員最頭疼的事情。所以,你選擇吧,是要安全還是要效率。

其實第一章接下來講的東西對於初學者來說已經並不重要了,以上你的知識你明白了之後,在接下來的學習上你會輕鬆很多。我建議你不要看的那麼清晰了。我想我們應該開始,馬上進入第二章了。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章