Java IAQ(Java最不常被回答的問題) 系列之一

問:神馬是IAQ(最不常被回答的問題)?

     

一個問題不常被人回答緣由有多種,好比較少的人知道答案,或者是問題是一個不起眼的,大多數人沒有注意到(但這個問題可能對你來講是很是重要的)。咱們能夠找到不少JAVA FAQs(最常被問到的問題),可是這裏只有最不常被回答的問題。html

問:在finally塊中的代碼有沒有不執行的狀況?


有,這裏有一個例子,在忽略choice的值的狀況下,finally中的代碼將不會執行。java

try {
    if (choice) {
      while (true) ;
    } else {
      System.exit(1);
    }
  } finally {
    code.to.cleanup();
  }

問:在一個類C中的方法m中,是否this.getClass()老是C。

     
  不是的,對於一些對象來講,在C的子類C1中沒有C1.m.()方法或者一些方法調用了super.m()的時候,返回的是C1。在這兩種狀況下,this.getClass()是C1,而不是C,除非C用final修飾了。

問:我定義了一個equals方法,可是HashTable忽略了它,爲何?

    
  equals方法對於大多數人來說很難正確的使用。這裏有一個例子咱們能夠看看如下這些問題:
一、你定義了一個錯誤的equals方法,例如你這樣寫:
public class C {
  public boolean equals(C that) { return id(this) == id(that); }
}
     可是爲了讓table.get(c)正確的執行,你須要保證equals方法接收一個Object參數,而不是C
public class C {
  public boolean equals(Object that) { 
    return (that instanceof C) && id(this) == id((C)that); 
  } 
}
     爲何?咱們查看HashTable的源碼就知道,由於HashTable的get看起來像這樣:
public class Hashtable {
  public Object get(Object key) {
    Object entry;
    ...
    if (entry.equals(key)) ...
  }
}
     如今entry.equals(key)方法調用的實際類型取決於實際運行時entry引用的對象類型。因此當你調用table.get(new C(...))的時候,他就在類C中查找帶的參數爲Object的equals方法。若是你恰好定義了這樣一個參數爲C的方法,那這個方法就可有可無了。他會忽略這個方法,尋找一個equals(Object),最終他會找到Obejct.equals(Object)方法。若是你想重寫這個方法,你必須保證參數與父類的方法對應。在某些狀況下你想要兩個方法,這樣你就不用操心你到底使用的是一個什麼類型的對象了。
public class C {
  public boolean equals(Object that) {
    return (this == that) 
            || ((that instanceof C) && this.equals((C)that)); 
  }

  public boolean equals(C that) { 
    return id(this) == id(that); // Or whatever is appropriate for class C
  } 
}
二、你在實現equals方法的時候沒有保證真正的相等。Equals必須具備對稱性、傳遞性、自反性。對稱性就是說a.equals(b)返回的結果必定要和b.equals(a)返回的結果相等。(這一點不少人都忽視了)。傳遞性,意味着若a.equals(b),b.equals(c)那麼必有a.equals(c)。自反性意味着a.equals(a)必須爲true。
三、你忘了hashCode方法。任什麼時候候你定義一個equals方法的時候,你應該同時定義一個hashCode方法。你必須保證兩個equals對象擁有相同的hashCode,若是你想讓hashTable正確的運行的話,你還應該保證不相等的對象(equals返回false)擁有不相等的hashcode。一些類將hashcode緩存進了對象的一個私有的變量中,因此hashcode僅僅須要被計算一次。若是你爲了節省時間而不定義hashCode方法的話,可能會致使錯誤。
四、你沒有正確的處理繼承關係。首先,考慮有沒有可能兩個不一樣類的對象是equal的。你確定會說(不!固然不是!)如今,來看看這個例子。一個類Rectangle擁有width屬性和height屬性,一個Box類也有這兩個屬性,另外又加了一個depth屬性。一個depth=0的Box等效於一個Rectangle?你可能會說是的。若是你在處理一個沒有用final修飾的類的時候,你的類可能被繼承,你想讓你的子類使用父類C的equals方法,像這樣:
public class C2 extends C {

  int newField = 0;

  public boolean equals(Object that) {
    if (this == that) return true;
    else if (!(that instanceof C2)) return false;
    else return this.newField == ((C2)that).newField && super.equals(that);
  }

}

爲了使它正確執行,你必須當心若是對待equals方法。例如,檢查參數是不是C的對象,要使用instanceof C而不是that.getClass()==C.class.在equals方法中使用this.getClass()==that.getClass()的時候你必須保證兩個對象是同一個類型的。緩存

五、你沒有正確處理循環引用。
public class LinkedList {

  Object contents;
  LinkedList next = null;

  public boolean equals(Object that) {
    return (this == that) 
      || ((that instanceof LinkedList) && this.equals((LinkedList)that)); 
  }

  public boolean equals(LinkedList that) { // Buggy!
   return Util.equals(this.contents, that.contents) &&
          Util.equals(this.next, that.next); 
  }

}
這裏我假設有一個工具類:
public static boolean equals(Object x, Object y) {
    return (x == y) || (x != null && x.equals(y));
  }


若是你試圖比較他們兩個(相互持有引用,即形成了循環引用),LinkedList.equals方法將永遠不會返回。

未完待續....

翻譯水平有限,輕噴。app

OSCHINA.NET原創翻譯/原文連接工具

相關文章
相關標籤/搜索