<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 6.2 繼承的語法 繼承與Java(以及其他OOP語言)非常緊密地結合在一起。我們早在第1章就為大家引入了繼承的概念,并在那章之后到本章之前的各章里不時用到,因為一些特殊的場合要求必須使用繼承。除此以外,創建一個類時肯定會進行繼承,因為若非如此,會從Java的標準根類`Object`中繼承。 用于組合的語法是非常簡單且直觀的。但為了進行繼承,必須采用一種全然不同的形式。需要繼承的時候,我們會說:“這個新類和那個舊類差不多。”為了在代碼里表面這一觀念,需要給出類名。但在類主體的起始花括號之前,需要放置一個關鍵字`extends`,在后面跟隨“基類”的名字。若采取這種做法,就可自動獲得基類的所有數據成員以及方法。下面是一個例子: ``` //: Detergent.java // Inheritance syntax & properties class Cleanser { private String s = new String("Cleanser"); public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } public void print() { System.out.println(s); } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); x.print(); } } public class Detergent extends Cleanser { // Change a method: public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Call base-class version } // Add methods to the interface: public void foam() { append(" foam()"); } // Test the new class: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); x.print(); System.out.println("Testing base class:"); Cleanser.main(args); } } ///:~ ``` 這個例子向大家展示了大量特性。首先,在`Cleanser append()`方法里,字符串同一個`s`連接起來。這是用`+=`運算符實現的。同`+`一樣,`+=`被Java用于對字符串進行“重載”處理。 其次,無論`Cleanser`還是`Detergent`都包含了一個`main()`方法。我們可為自己的每個類都創建一個`main()`。通常建議大家象這樣進行編寫代碼,使自己的測試代碼能夠封裝到類內。即便在程序中含有數量眾多的類,但對于在命令行請求的`public`類,只有`main()`才會得到調用。所以在這種情況下,當我們使用`java Detergent`的時候,調用的是`Degergent.main()`——即使`Cleanser`并非一個`public`類。采用這種將`main()`置入每個類的做法,可方便地為每個類都進行單元測試。而且在完成測試以后,毋需將`main()`刪去;可把它保留下來,用于以后的測試。 在這里,大家可看到`Deteregent.main()`對`Cleanser.main()`的調用是明確進行的。 需要著重強調的是`Cleanser`中的所有類都是`public`屬性。請記住,倘若省略所有訪問指示符,則成員默認為“友好的”。這樣一來,就只允許對包成員進行訪問。在這個包內,任何人都可使用那些沒有訪問指示符的方法。例如,`Detergent`將不會遇到任何麻煩。然而,假設來自另外某個包的類準備繼承`Cleanser`,它就只能訪問那些`public`成員。所以在計劃繼承的時候,一個比較好的規則是將所有字段都設為`private`,并將所有方法都設為`public`(`protected`成員也允許派生出來的類訪問它;以后還會深入探討這一問題)。當然,在一些特殊的場合,我們仍然必須作出一些調整,但這并不是一個好的做法。 注意`Cleanser`在它的接口中含有一系列方法:`append()`,`dilute()`,`apply()`,`scrub()`以及`print()`。由于`Detergent`是從`Cleanser`派生出來的(通過`extends`關鍵字),所以它會自動獲得接口內的所有這些方法——即使我們在`Detergent`里并未看到對它們的明確定義。這樣一來,就可將繼承想象成“對接口的重復利用”或者“接口的復用”(以后的實現細節可以自由設置,但那并非我們強調的重點)。 正如在`scrub()`里看到的那樣,可以獲得在基類里定義的一個方法,并對其進行修改。在這種情況下,我們通常想在新版本里調用來自基類的方法。但在`scrub()`里,不可只是簡單地發出對`scrub()`的調用。那樣便造成了遞歸調用,我們不愿看到這一情況。為解決這個問題,Java提供了一個`super`關鍵字,它引用當前類已從中繼承的一個“超類”(Superclass)。所以表達式`super.scrub()`調用的是方法`scrub()`的基類版本。 進行繼承時,我們并不限于只能使用基類的方法。亦可在派生出來的類里加入自己的新方法。這時采取的做法與在普通類里添加其他任何方法是完全一樣的:只需簡單地定義它即可。`extends`關鍵字提醒我們準備將新方法加入基類的接口里,對其進行“擴展”。`foam()`便是這種做法的一個產物。 在`Detergent.main()`里,我們可看到對于`Detergent`對象,可調用`Cleanser`以及`Detergent`內所有可用的方法(如`foam()`)。 ## 6.2.1 初始化基類 由于這兒涉及到兩個類——基類及派生類,而不再是以前的一個,所以在想象派生類的結果對象時,可能會產生一些迷惑。從外部看,似乎新類擁有與基類相同的接口,而且可包含一些額外的方法和字段。但繼承并非僅僅簡單地復制基類的接口了事。創建派生類的一個對象時,它在其中包含了基類的一個“子對象”。這個子對象就象我們根據基類本身創建了它的一個對象。從外部看,基類的子對象已封裝到派生類的對象里了。 當然,基類子對象應該正確地初始化,而且只有一種方法能保證這一點:在構造器中執行初始化,通過調用基類構造器,后者有足夠的能力和權限來執行對基類的初始化。在派生類的構造器中,Java會自動插入對基類構造器的調用。下面這個例子向大家展示了對這種三級繼承的應用: ``` //: Cartoon.java // Constructor calls during inheritance class Art { Art() { System.out.println("Art constructor"); } } class Drawing extends Art { Drawing() { System.out.println("Drawing constructor"); } } public class Cartoon extends Drawing { Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } } ///:~ ``` 該程序的輸出顯示了自動調用: ``` Art constructor Drawing constructor Cartoon constructor ``` 可以看出,構建是在基類的“外部”進行的,所以基類會在派生類訪問它之前得到正確的初始化。 即使沒有為`Cartoon()`創建一個構造器,編譯器也會為我們自動生成一個默認構造器,并發出對基類構造器的調用。 (1) 含有參數的構造器 上述例子有自己默認的構造器;也就是說,它們不含任何參數。編譯器可以很容易地調用它們,因為不存在具體傳遞什么參數的問題。如果類沒有默認的參數,或者想調用含有一個參數的某個基類構造器,必須明確地編寫對基類的調用代碼。這是用`super`關鍵字以及適當的參數列表實現的,如下所示: ``` //: Chess.java // Inheritance, constructors and arguments class Game { Game(int i) { System.out.println("Game constructor"); } } class BoardGame extends Game { BoardGame(int i) { super(i); System.out.println("BoardGame constructor"); } } public class Chess extends BoardGame { Chess() { super(11); System.out.println("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); } } ///:~ ``` 如果不調用`BoardGames()`內的基類構造器,編譯器就會報告自己找不到`Games()`形式的一個構造器。除此以外,在派生類構造器中,對基類構造器的調用是必須做的第一件事情(如操作失當,編譯器會向我們指出)。 (2) 捕獲基本構造器的異常 正如剛才指出的那樣,編譯器會強迫我們在派生類構造器的主體中首先設置對基類構造器的調用。這意味著在它之前不能出現任何東西。正如大家在第9章會看到的那樣,這同時也會防止派生類構造器捕獲來自一個基類的任何異常事件。顯然,這有時會為我們造成不便。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看