越來越發(fā)現(xiàn)這是一本難得的好書,Java程序員不看這本書的話真是很遺憾。本章講述的是類和接口相關(guān)的問題。這幾個(gè)Item都非常重要.

Item 12:把類和成員的可訪問范圍降到最低

    好的模塊設(shè)計(jì)應(yīng)該盡最大可能封裝好自己的內(nèi)部信息,這樣可以把模塊之間的耦合程度降到最低。開發(fā)得以并行,無疑這將加快開發(fā)的速度,便于系統(tǒng)地維護(hù)。Java中通過訪問控制符來解決這個(gè)問題。

  1. public表示這個(gè)類在任何范圍都可用。
  2. protected表示只有子類和包內(nèi)的類可以使用
  3. private-package(default)表示在包內(nèi)可用
  4. private表示只有類內(nèi)才可以用

你在設(shè)計(jì)一個(gè)類的時(shí)候應(yīng)該盡量的按照4321得順序設(shè)計(jì)。如果一個(gè)類只是被另一個(gè)類使用,那么應(yīng)該考慮把它設(shè)計(jì)成這個(gè)類的內(nèi)部類。通常public的類不應(yīng)該有public得字段,不過我們通常會(huì)用一個(gè)類來定義所有的常量,這是允許的。不過必須保證這些字段要么是基本數(shù)據(jù)類型要么引用指向的對(duì)象是不可修改的。不然他們將可能被修改。例如下面的定義中data就是不合理的,后面兩個(gè)沒有問題。
public class Con
{
      public static final int[] data = {1,2,3};// it is bad
      public static final String hello = "world";
      public static final int i = 1;
}

Item 13:不可修改的類更受青睞

    不可修改的類意思是他們一經(jīng)創(chuàng)建就不會(huì)改變,例如String類。他們的設(shè)計(jì)、實(shí)現(xiàn)都很方便,安全性高——它們是線程安全的。設(shè)計(jì)不可修改類有幾點(diǎn)規(guī)則:

  1. 不要提供任何可以修改對(duì)象的方法
  2. 確保沒有方法能夠被覆蓋,可以通過把它聲明為final
  3. 所有字段設(shè)計(jì)成final
  4. 所有字段設(shè)計(jì)成private
  5. 確保外部不能訪問到類的可修改的組件
    不可修改類也有個(gè)缺點(diǎn)就是創(chuàng)建不同值得類的時(shí)候要?jiǎng)?chuàng)建不同的對(duì)象,String就是這樣的。通常有個(gè)解決的辦法就是提供一個(gè)幫助類來彌補(bǔ),例如StringBuffer類。

Item 14:化合(合成)比繼承更值得考慮

      實(shí)現(xiàn)代碼重用最重要的辦法就是繼承,但是繼承破壞了封裝,導(dǎo)致軟件的鍵壯性不足。如果子類繼承了父類,那么它從父類繼承的方法就依賴父類的實(shí)現(xiàn),一旦他改變了會(huì)導(dǎo)致不可預(yù)測(cè)的結(jié)果。作者介紹了InstrumentedHashSet作為反例進(jìn)行說明,原因就是沒有明白父類的方法實(shí)現(xiàn)。作者給出的解決辦法是通過化合來代替繼承,用包裝類和轉(zhuǎn)發(fā)方法來解決問題。把想擴(kuò)展的類作為本類的一個(gè)private final得成員變量。把方法參數(shù)傳遞給這個(gè)成員變量并得到返回值。這樣做的缺點(diǎn)是這樣的類不適合回掉框架。繼承雖然好,我們卻不應(yīng)該濫用,只有我們能確定它們之間是is-a得關(guān)系的時(shí)候才使用。

Item 15:如果要用繼承那么設(shè)計(jì)以及文檔都要有質(zhì)量保證,否則就不要用它

    為了避免繼承帶來的問題,你必須提供精確的文檔來說明覆蓋相關(guān)方法可能出現(xiàn)的問題。在構(gòu)造器內(nèi)千萬不要調(diào)用可以被覆蓋的方法,因?yàn)樽宇惛采w方法的時(shí)候會(huì)出現(xiàn)問題。
import java.util.*;

public class SubClass extends SuperClass
{
 private final Date date;
 
 public SubClass()
 {
  date = new Date(); 
 }
 
 public void m()
 {
  System.out.println(date); 
 }
 
 public static void main(String[] args)
 {
  SubClass s = new SubClass();
  s.m(); 
 }
 
}

class SuperClass
{
 public SuperClass()
 {
  m(); 
 } 
 
 public void m()
 {
  
 }
}
由于在date被初始化之前super()已經(jīng)被調(diào)用了,所以第一次輸出null而不是當(dāng)前的時(shí)間。
由于在Clone()或者序列化的時(shí)候非常類似構(gòu)造器的功能,因此readObject()和clone()方法內(nèi)最好也不要包括能被覆蓋的方法。

Item 16:在接口和抽象類之間優(yōu)先選擇前者

      接口和抽象類都用來實(shí)現(xiàn)多態(tài),不過我們應(yīng)該優(yōu)先考慮用接口。知道嗎?James說過如果要讓他重新設(shè)計(jì)java的話他會(huì)把所有都設(shè)計(jì)成接口的。抽象類的優(yōu)點(diǎn)是方便擴(kuò)展,因?yàn)樗潜焕^承的,并且方法可以在抽象類內(nèi)實(shí)現(xiàn),接口則不行。

Item 17:接口只應(yīng)該用來定義類型

      接口可以這樣用的 Collection c = new xxxx();這是我們最常用的。不要把接口用來做其他的事情,比如常量的定義。你應(yīng)該定義一個(gè)類,里面包含public final static 得字段。

Item 18: 在靜態(tài)和非靜態(tài)內(nèi)部類之間選擇前者

      如果一個(gè)類被定義在其他的類內(nèi)部那么它就是嵌套類,可以分為靜態(tài)內(nèi)部類、非靜態(tài)內(nèi)部類和匿名類。
   static member class 得目的是為enclosing class服務(wù),如果還有其他的目的,就應(yīng)該把它設(shè)計(jì)成top-level class。nonstatic member class是和enclosing class instance關(guān)聯(lián)的,如果不需要訪問enclosing class instance的話應(yīng)該把它設(shè)計(jì)成static得,不然會(huì)浪費(fèi)時(shí)間和空間。anonymous class是聲明和初始化同時(shí)進(jìn)行的。可以放在代碼的任意位置。典型應(yīng)用是Listener 和process object例如Thread。