j2ee面試寶典翻譯(3) j2ee job interview companion

來源:互聯網
上載者:User

Q9:如何讓表達“是一個”和“有一個”關係。或者請解釋下“繼承”和“組合”。組合和彙總之間有什麼區別。

A9:“是一個”的關係表示繼承而“有一個”的關係是表示組合。繼承和組合都允許你將子物件放入新類中。代碼重用的兩個主要技術便是類繼承和對象組合。

繼承是單向的。例如房子是一棟建築,但建築不是一個房子。繼承使用extends關鍵字。

組合:用於表達房子有一個浴室。說房子是一個浴室就不準確了。組合簡單地使用執行個體變數引用其他對象,如House類擁有一個執行個體變數,引用一個Bathroom對象。


Q:哪一個更好。組合還是繼承。

A:指南是,僅當子類“是一個”父類時,才使用繼承。 不要僅僅為了代碼重用而使用繼承。如果沒有“是一個”的關係,就應該使用組合文法。過度使用實現繼承(使用“擴充”關鍵字)會破壞所有的子類,特別在超類需要被修改的時候。 不要僅僅為了獲得多態性而使用繼承。如果沒有“是一個”的關係,而你又想要獲得多態性,那麼,使用介面繼承和組合文法。

Q:組合和彙總有什麼區別。

A:它們都表達整體和局部的關係。彙總關係,局部可以獨立於整體而存在。例如,一個行項目和產品是整體和局部的關係。如果一個行項目被刪除,對應的產品不需要被刪除。所以彙總是一種較弱的關係。組合關係,局部不可獨立於整體而存在。如果一個整體被刪除,那麼所有零件也會被刪除。例如,訂單和項目是整體和局部的關係。如果一個訂單被刪除,那麼相應的行項目也應該被刪除。所以組合具有更強的關係。


Q10:你怎麼理解繼承、封裝、多態和動態綁定?

A10:Polymorphism多態——描述了這樣一種能力,一個給定類型的變數可以被用來引用多個類型不同的對象(當然,需要這些類型是給定類型的子類),而調用的卻是這個變數引用的對象的具體類型上的方法。簡而言之,多態是一種自下而上的方法調用。多態的好處是,很容易添加新的擴充類而不破壞原有的調用代碼。當給一個對象發送一條訊息(調用方法)時,你甚至不知道這個對象的具體類型,但是正確的行為會發生,這就是多態。

物件導向程式設計語言實現多態的過程稱為動態綁定。(運行期類型推斷。)

Inheritance繼承——是將基類的行為(即方法)和狀態(即變數)包含到衍生類別中,這樣它們就可在衍生類別中被訪問了。關鍵的好處是,它提供了代碼重用的正式機制。

任何商務邏輯的公用部分都可以從衍生類別移至基類,重構時這樣做,可以避免代碼重複而提高代碼的可維護性。

現有的類被稱為基類而衍生類別被稱為子類。繼承也可以被定義為一個過程,即對象獲得一個或多個其他對象的特徵的過程,就像孩子從父母那裡獲得特徵一樣。

有兩種類型的繼承:

1、實作繼承:可以通過繼承部分或全部父類中已經實現的功能來擴充程式。在Java中,您只可以從一個超類繼承。實作繼承提升了重用性,但是不正確的繼承使用可能導致編程噩夢,因為它會破壞封裝性並且為將來的變化帶來問題。使用實作繼承,子類變得和父類緊密耦合起來。這將使得設計變得脆弱,如果你想改變父類,就不得不瞭解子類的細節以免破壞他們。所以使用實作繼承,確保子類只依賴父類的行為,而不是實際的實現。

2、介面繼承:介面提供了一種機制,將無關的類聯絡起來——通過指定系列普通方法,這些實作類別都必須包含。(實作類別之間可以是互不相關的。)介面繼承提升了“面向介面編程而不是面向實現編程”的原則。這樣降低了系統之間的耦合。在Java中,你可以實現任意數量的介面。這比“實作繼承”更靈活,因為它不會把你鎖定在具體實現中,具體實現會使子類變得難以維護。也要小心,修改介面會破壞實作類別。

Which one to use?優先選擇介面,因為它符合“面向介面編程”的理念並且可以降低耦合。介面繼承可以在對象組合的協助下實現代碼的重用。如果你看GOF設計模式,你會發現他們更偏愛介面繼承而不是實作繼承。

實作繼承案例:

package ch08_extends3;/** * 假設活期存款和定期存款在存取行為上有類型的行為,我們把這兩個行為的實現定義在父類中。 * <p>但是活期存款和定期存款在計算利息這個行為上表現是不同的。 *@author zhengwei 2013-7-13 */public  abstract  class  Account   {     public void deposit (double amount) {           System.out.println("depositing " + amount);     }      public void withdraw (double amount) {           System.out.println ("withdrawing " + amount);     }          public abstract double calculateInterest (double  amount); }  class SavingsAccount  extends Account  {       public double  calculateInterest  (double amount) {          // calculate interest for SavingsAccount          return amount * 0.03;      }           public void  deposit (double amount) {           super.deposit  (amount);  // get code reuse          // do something else      }       public void  withdraw (double amount) {          super.withdraw  (amount);  // get code reuse           // do something else      }   }  class TermDepositAccount extends Account  {      public double  calculateInterest (double amount) {         // calculate interest for SavingsAccount         return amount * 0.05;     }          public void deposit(double amount) {         super.deposit  (amount);  // get code reuse         // do something else     }      public void withdraw(double amount) {         super.withdraw  (amount); //  get code reuse         // do something else     } } 

介面繼承案例:

package ch08_extends3;/** * 介面繼承範例程式碼,使用組合來重用代碼。 * <p>在下例中,deposite和withdraw方法共用了AccountHelper中的程式碼片段。 * <p>而calculateInterest方法在各自實現中有獨特的實現 * @author zhengwei 2013-7-13 */public interface Account {public abstract double calculateInterest(double amount);public abstract void deposit(double amount);public abstract void withdraw(double amount);}interface AccountHelper {public abstract void deposit(double amount);public abstract void withdraw(double amount);}/** * class AccountHelperImpl has reusable code as methods deposit (double amount) * and withdraw (double amount).  * <p>AccountHelperImpl含有可重用代碼:deposit方法和withdraw方法 */class AccountHelperImpl implements AccountHelper {public void deposit(double amount) {System.out.println("depositing " + amount);}public void withdraw(double amount) {System.out.println("withdrawing " + amount);}}class SavingsAccountImpl implements Account {// composed helper class (i.e. composition ).AccountHelper helper = new AccountHelperImpl();public double calculateInterest(double amount) {// calculate interest for SavingsAccountreturn amount * 0.03;}public void deposit(double amount) {helper.deposit(amount); // code reuse via composition}public void withdraw(double amount) {helper.withdraw(amount); // code reuse via composition}}class TermDepositAccountImpl implements Account {// composed helper class (i.e. composition ).AccountHelper helper = new AccountHelperImpl();public double calculateInterest(double amount) {// calculate interest for SavingsAccountreturn amount * 0.05;}public void deposit(double amount) {helper.deposit(amount); // code reuse via composition}public void withdraw(double amount) {helper.withdraw(amount); // code reuse via composition}}

兩種方式可以使用如下的測試代碼:

package ch08_extends3;/** *  * @author zhengwei 2013-7-13 */public class Test {public static void main(String[] args) {Account acc1 = new SavingsAccountImpl();acc1.deposit(50.0);Account acc2 = new TermDepositAccountImpl();acc2.deposit(25.0);acc1.withdraw(25);acc2.withdraw(10);double cal1 = acc1.calculateInterest(100.0);double cal2 = acc2.calculateInterest(100.0);System.out.println("Savings --> " + cal1);System.out.println("TermDeposit -->  " + cal2);}}

輸出結果:

depositing 50.0
depositing 25.0
withdrawing 25.0
withdrawing 10.0
Savings --> 3.0
TermDeposit -->  5.0

問:為什麼優先通過組合來重用代碼而不是繼承?

答:可以看到兩種方式都可利用多態,並重用了代碼,結果也是一致的,但是: 類繼承的優點是,它的重用是在編譯時間靜態地完成的,是便於使用的。類繼承的缺點也是因為它是靜態,從父類繼承而來的實現在運行期不能被改變。而在對象組合中,(組合進來的)功能是在運行期動態獲得的,通過對象收集其他對象的引用來達成。這種方法的優點是,組合進來的“實作對象”在運行時是可以更換的。這是因為我們依賴的是對象的介面類型,調用對象也只有通過他們的介面,所以一個對象可以被替換為另一個,只要他們有相同的類型(介面)。例如:組合進來的類型AccountHelperImpl可以在有需要時被替換為一個更有效率的實現:

public class  EfficientAccountHelperImpl  implements AccountHelper  {     public void  deposit(double amount) {         System.out.println(" efficient depositing " + amount);     }      public void withdraw(double amount) {         System.out.println(" efficient withdrawing " + amount);     } }

譯註:感覺這裡沒說透。我來說下這個問題,“父母是不可以動態替換的,但是朋友可以是動態替換的”。一旦繼承了某類,你不可能替換這種繼承關係,但是組合,我們可以通過向構造器傳參或者setter方法臨時改變組裝進來的對象的類型,當然前提是這些對象的類型否和依賴的介面類型。

再進一步,重用代碼要麼用super.someMethod(),這個super指向父類對象,這個super你是沒法換的。要麼是通過brother.someMethod()重用代碼,這個brother是組合文法中的域成員,它指向和我們協作的,或者說依賴的對象,這種對象只需一個set方法就可替換了。
另一個問題是,實作繼承中,子類依賴父類實現。這使得子類難以被重用,特別是繼承而來的實作不再令人滿意並因此而破壞封裝性(譯註:沒看懂。)另外,對父類的修改不僅會沿著繼承層次影響子類,還會影響到單純使用子類的其他代碼,這種子類嚴重耦合父類的設計是非常脆弱的。但是改變組合對象的介面/實現是容易的。

還是我上面說的那點。

因為對象組合的靈活性和強大,大部分設計模式只要有可能,就優先強調對象組合而非繼承。很多時候,一個設計模式使用組合就展示了一個聰明的辦法來解決一類常見問題,而不是用標準的、不那麼靈活的基於繼承的解決方案。

Encapsulation封裝——指的是保持所有相關成員(變數和方法)在一起,在一個對象中。指定成員變數為私人可以隱藏變數和方法。對象應該向外界隱藏他們的內部運作。好的封裝提高代碼模組化,通過防止對象以一種意想不到的方式相互作用,從而使未來的開發和重構工作更容易。

範例程式碼:

class MyMarks {private int vmarks = 0;private String name;public void setMarks(int mark) throws MarkException {if (mark > 0)this.vmarks = mark;else {throw new MarkException("No negative Values");}}public int getMarks() {return vmarks;}// getters and setters for attribute name goes here.}

能夠封裝類的成員對於安全性和完整性來說是極其重要的。我們可以保護變數接收不合法的值。上面的範例程式碼描述了如何通過封裝來保護MyMarks不擁有負值。任何修改成員變數”vmarks”的行為必須通過setter方法setMarks(int)。這可以防止對象”MyMarks”擁有負值,調用者傳入負值將得到一個異常。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.