<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    每日一得

    不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速開發(fā)
    最近關(guān)心的內(nèi)容:SSH,seam,flex,敏捷,TDD
    本站的官方站點是:顛覆軟件

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      220 隨筆 :: 9 文章 :: 421 評論 :: 0 Trackbacks
    終于翻開這本James都稱贊的java經(jīng)典書籍了,發(fā)現(xiàn)比一般的英語書籍要難懂一些。但是里面的Item都是非常實用的,是java程序員應(yīng)該理解的。

    Creating and Destroying Object

    Item 1:考慮用靜態(tài)工廠方法替代構(gòu)造器
    例如:public static Boolean valueOf(boolean b)
    ?????{
    ??????????return (b?Boolean.TRUE:Boolean.FALSE);
    ?????}
    這樣的好處是方法有名字,并且它可以復(fù)用對象,不像構(gòu)造器每次調(diào)用都產(chǎn)生新的對象。其次它還可以返回返回類型的子類。不好的地方是如果沒有public or protected構(gòu)造器的類將不能被繼承。還有就是靜態(tài)工廠方法的名字和其他的靜態(tài)方法名字不容易區(qū)分。

    Item 2:通過添加私有構(gòu)造器來加強單例屬性(singletom property)
    例如:public class Hello
    ????????????{
    ??????????????????private static final Hello Instance = new Hell();

    ??????????????????private Hello()
    ??????????????????{}
    ????????????????????
    ????????????????????public static Hello getInstance()
    ??????????????????????{
    ?????????????????????return Instance;

    ?????????????????????}
    ????????????}
    這個私有構(gòu)造器只能在內(nèi)部被使用,確保了單例模式!
    Item 3:避免創(chuàng)建重復(fù)的對象
    對不可修改的對象盡量進行復(fù)用,這樣效率和性能都會提高。例如如果循環(huán)100次String s = new String("hello")將創(chuàng)建100個對象 循環(huán)100次String s = "hello";則只創(chuàng)建了一個對象。很好的進行了復(fù)用。

    Item 4:用私有構(gòu)造器來避免被實例化

    例如public UtilityClass
    {
    ???private UtilityClass()
    ?? {}

    ///
    }
    通常那些工具類是這么設(shè)計的

    Item 5:消除絕對的對象引用

    ?????雖然java中使用gc來管理內(nèi)存,但是如果不注意的話也會產(chǎn)生“內(nèi)存泄漏”。例如下面的程序
    public class Stack
    {
    ?private Object[] elements;
    ?private int size = 0;
    ?
    ?public Stack(int i)
    ?{
    ??this.elements = new Object[i];?
    ?}?
    ?
    ?public void push(Object e)
    ?{
    ??ensure();
    ??elements[size++] = e;?
    ?}
    ?
    ?public Object pop()
    ?{
    ??if(size == 0)
    ??{
    ???////?
    ??}?
    ??
    ??return elements[size--];
    ?}
    ?
    ?private void ensure()
    ?{
    ??////?
    ?}
    }
    標記的地方存在著內(nèi)存泄漏的問題,因為當他被彈出棧的時候,它也沒有成為可回收的垃圾對象,Stack維護著他們的絕對的引用。將不能更改。改進的方法是如下的寫法
    ?public Object pop()
    ?{
    ??if(size == 0)
    ??{
    ???////?
    ??}?
    ??Object obj = elements[--size];
    ??elements[size] = null;
    ??
    ??return obj;
    ?}
    ?但是切忌不要濫用null。

    Item 6:避免finalizer
    垃圾回收器是低線程級別運行的且不能被強迫執(zhí)行。System.gc()只是建議垃圾回收器收集垃圾,它可不一定馬上運行,而且垃圾回收器運行的時候會掛起其他線程導(dǎo)致程序停止響應(yīng)。推薦使用的方法類似于
    InputStream is = null;

    try
    {
    ??????is = /////;
    }
    finally
    {
    ??????is.close();
    }

    Methods Common to All Objects

    ?

    item 7:當你覆蓋equals方法的時候一定要遵守general contact

    ?

    ???覆蓋equals的時候一定要加倍的小心,其實最好的辦法就是不覆蓋這個方法。比如在下面的情況下就可以不覆蓋

    ???1這個類的每個實例都是唯一的,例如Thread

    ???2 如果你不關(guān)心這個類是否該提供一個測試邏輯相等的方法

    ???3超類已經(jīng)覆蓋了equals方法,并且它合適子類使用

    ???4如果這個類是private或者是package-private的,并且你確信他不會被調(diào)用

    ?

    ???但是當我們要為這個類提供區(qū)分邏輯相等和引用相等的方法的時候,我們就必須要覆蓋這個方法了。例如String類,Date類等,覆蓋的時候我們一定要遵從general contact,說白了就是一個合同。合同的主要內(nèi)容是

    ???1x.equals(x)必須返回true

    ???2x.equalsy)當且僅當y.equals(x)返回true的時候返回true

    ???3x.equals(y)返回truey.equals(z)返回true,那么x.equals(z)必須返回true

    ???4.如果沒有任何修改得話那么多次調(diào)用x.equals(y)的返回值應(yīng)該不變

    ???5.任何時候非空的對象x,x.equals(null)必須返回false

    下面是作者的建議如何正確的覆蓋equals方法

    1.? ==檢查是否參數(shù)就是這個對象的引用

    2.? instanceof判斷參數(shù)的類型是否正確

    3.? 把參數(shù)轉(zhuǎn)換成合適的類型

    4.? 比較類的字段是不是匹配

    例如:

    public boolean equals(Object o)

    {

    ?????? if(o== this) return true;

    ?????? if(!(o instanceof xxxx) return false;

    ?????? xxx in = (xxx)o;

    ?????? return ……..

    }

    最后一點要注意的時候不要提供這樣的方法public boolean equals(MyClass o)這樣是重載并不是覆蓋Objectequals方法

    item 8 :當你覆蓋equals的時候必須覆蓋hashCode方法

    ??? 這點必須切忌,不然在你和hash-based集合打交道的時候,錯誤就會出現(xiàn)了。關(guān)鍵問題在于一定要滿足相等的對象必須要有相等的hashCode。如果你在PhoneNumber類中覆蓋了equals方法,但是沒有覆蓋hashCode方法,那么當你做如下操作的時候就會出現(xiàn)問題了。

    Map m = new HashMap();

    m.put(new PhoneNumber(408,863,3334),”ming”)
    當你調(diào)用m.get(new PhoneNumber(408,863,3334))的時候你希望得到ming但是你卻得到了null,為什么呢因為在整個過程中有兩個PhoneNumber的實例,一個是put一個是get,但是他們兩個邏輯相等的實例卻得到不同的hashCode那么怎么可以取得以前存入的ming呢。

    ?

    Item 9:永遠覆蓋toString方法

    ??? 在ObjecttoString方法返回的形式是Class的類型加上@加上16進制的hashcode。你最好在自己的類中提供toString方法更好的表述實例的信息,不然別人怎么看得明白呢。

    ?

    Item 10:覆蓋clone()方法的時候一定要小心

    ??? 一個對象要想被Clone,那么要實現(xiàn)Clone()接口,這個接口沒有定義任何的方法,但是如果你不實現(xiàn)這個接口的話,調(diào)用clone方法的時候會出現(xiàn)CloneNotSupportedException,這就是作者叫做mixin的接口類型。通常clone()方法可以這樣覆蓋

    public Object clone()

    {

    try
    {

    ????????????? return super.clone();

    }

    catch(CloneNotSupportedException e)
    {}

    }

    但是當你要clone的類里面含有可修改的引用字段的時候,那么你一定要把整個類的藍圖進行復(fù)制,如果對你clone得到的對象進行修改的時候還會影響到原來的實例,那么這是不可取的。所以應(yīng)該這樣clone()

    public Object clone() throws CloneNotSupportedException

    {

    ?????? Stack Result? = (Stack)super.clone();

    ?????? Result.elements = (Object[])elements.clone();

    ?????? Return result;

    }

    其中elementsstack類中可修改的引用字段,注意如果elementsfinal的話我們就無能為力了,因為不能給他重新賦值了.其實如果不是必須的話,根本就不用它最好。

    ?

    Item 11:考慮適當?shù)臅r候覆蓋Comparable接口

    ?????Thinking in java上說的更清楚,這里不多少了。

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

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

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

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

    你 在設(shè)計一個類的時候應(yīng)該盡量的按照4321得順序設(shè)計。如果一個類只是被另一個類使用,那么應(yīng)該考慮把它設(shè)計成這個類的內(nèi)部類。通常public的類不應(yīng) 該有public得字段,不過我們通常會用一個類來定義所有的常量,這是允許的。不過必須保證這些字段要么是基本數(shù)據(jù)類型要么引用指向的對象是不可修改 的。不然他們將可能被修改。例如下面的定義中data就是不合理的,后面兩個沒有問題。
    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)建就不會改變,例如String類。他們的設(shè)計、實現(xiàn)都很方便,安全性高——它們是線程安全的。設(shè)計不可修改類有幾點規(guī)則:

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

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

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

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

    ????為了避免繼承帶來的問題,你必須提供精確的文檔來說明覆蓋相關(guān)方法可能出現(xiàn)的問題。在構(gòu)造器內(nèi)千萬不要調(diào)用可以被覆蓋的方法,因為子類覆蓋方法的時候會出現(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而不是當前的時間。
    由于在Clone()或者序列化的時候非常類似構(gòu)造器的功能,因此readObject()和clone()方法內(nèi)最好也不要包括能被覆蓋的方法。

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

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

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

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

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

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

    ??? 由于以前學過C語言,所以對C還是蠻有感情,而JAVAC又有很多相似之處,很多從C轉(zhuǎn)過來學習JAVA的兄弟,可能一開始都不是很適應(yīng),因為很多在C里面的結(jié)構(gòu)在JAVA里面都不能使用了,所以下面我們來介紹一下C語言結(jié)構(gòu)的替代。

    ?????

    ????? Item 19:用類代替結(jié)構(gòu)

    ????? JAVA剛面世的時候,很多C程序員都認為用類來代替結(jié)構(gòu)現(xiàn)在太復(fù)雜,代價太大了,但是實際上,如果一個JAVA的類退化到只包含一個數(shù)據(jù)域的話,這樣的類與C語言的結(jié)構(gòu)大致是等價的。

    ????? 比方說下面兩個程序片段:

    ????? class Point

    ????? {

    ?????? private float x;

    ?????? private float y;

    ????? }

    ????? 實際上這段代碼和C語言的結(jié)構(gòu)基本上沒什么區(qū)別,但是這段代碼恐怕是眾多OO設(shè)計Fans所不齒的,因為它沒有體現(xiàn)封裝的優(yōu)異性,沒有體現(xiàn)面向?qū)ο笤O(shè)計的優(yōu)點,當一個域被修改的時候,你不可能再采取任何輔助的措施了,那我們再來看一看采用包含私有域和共有訪問方法的OO設(shè)計代碼段:

    ????? class Point

    ????? {

    ?????? private float x;

    ?????? private float y;

    ?????? public Point(float x,float y)

    ?????? {

    ???????????? this.x=x;

    ???????????? this.y=y;

    ?????? }

    ??????? public float getX(){retrun x;}

    ??????? public float getY(){return y;}

    ??????? public void setX(float x){this.x=x;}

    ??????? public void setY(float y){this.y=y;}

    ????? }

    ?

    ??????? 單從表面上看,這段代碼比上面那個多了很多行,還多了很多函數(shù),但是仔細想一下,這樣的OO設(shè)計,似乎更人性化,我們可以方面的對值域進行提取,修改等操作,而不直接和值域發(fā)生關(guān)系,這樣的代碼不僅讓人容易讀懂,而且很安全,還吸取了面向?qū)ο蟪绦蛟O(shè)計的靈活性,試想一下,如果一個共有類暴露它的值域,那么想要在將來的版本中進行修改是impossible的,因為共有類的客戶代碼已經(jīng)遍布各處了。

    需要提醒一點的是,如果一個類是包級私有的,或者是一個私有的嵌套類,則直接暴露其值域并無不妥之處。

    ?

    Item 20用類層次來代替聯(lián)合

    我們在用C語言來進行開發(fā)的時候,經(jīng)常會用到聯(lián)合這個概念,比如:

    ?????? typedef struct{

    ???? double length;

    ???? double width;????

    }rectangleDimensions_t;

    那我們在JAVA里面沒有聯(lián)合這個概念,那我們用什么呢?對!用繼承,這也是JAVA最吸引我的地方之一,它可以使用更好的機制來定義耽擱數(shù)據(jù)類型,在Bruce EckelThinking in java里面也多次提到了一個和形狀有關(guān)的例子,我們可以先籠統(tǒng)的定義一個抽象類,即我們通常所指的超類,每個操作定義一個抽象的方法,其行為取決于標簽的值,如果還有其他的操作不依賴于標簽的值,則把操作變成根類(繼承的類)中的具體方法。

    這樣做的最重要的優(yōu)點是:類層次提供了類型的安全性。

    其次代碼非常明了,這也是OO設(shè)計的優(yōu)點。

    而且它很容易擴展,即使是面向多個方面的工作,能夠同樣勝任。

    最后它可以反映這些類型之間本質(zhì)上的層次關(guān)系,從而允許更強的靈活性,以便編譯時類型檢查。

    ?

    Item 21用類來代替enum結(jié)構(gòu)

    Java程序設(shè)計語言提出了類型安全枚舉的模式來替代enum結(jié)構(gòu),它的基本思想很簡單:定義一個類來代表枚舉類型的單個元素,并且不提供任何公有的構(gòu)造函數(shù),相反,提供公有靜態(tài)final類,使枚舉類型中的每一個常量都對應(yīng)一個域。

    類型安全枚舉類型的一個缺點是,裝載枚舉類的和構(gòu)造常量對象時,需要一定的時間和空間開銷,除非是在資源很受限制的設(shè)備比如蜂窩電哈和烤面包機上,否則在實際中這個問題不會被考慮。

    ?總之,類型安全枚舉類型明顯優(yōu)于int類型,除非實在一個枚舉類型主要被用做一個集合元素,或者主要用在一個資源非常不受限的環(huán)境下,否則類型安全枚舉類型的缺點都不成問題,依次,在要求使用一個枚舉類型的環(huán)境下,我們首先應(yīng)考慮類型安全枚舉類型模式。

    ?

    Item 22用類和接口來代替函數(shù)指針

    眾所周知,JAVA語言和C的最大區(qū)別在于,前者去掉了指針,小生第一次接觸JAVA的時候覺得好不習慣,因為突然一下子沒了指針,覺得好不方面啊,C語言的精髓在于其指針的運用,而JAVA卻把它砍掉了,讓人好生郁悶,不過隨著時間的推移,我漸漸明白了用類和接口的應(yīng)用也同樣可以提供同樣的功能,我們可以直接定義一個這樣一個類,他的方法是執(zhí)行其他方法上的操作,如果一個類僅僅是導(dǎo)出這樣一個方法,那么它實際上就是一個指向該方法的指針,舉個例子:

    ?class StringLengthComprator{

    public int compare(String s1,String s2)

    {

    return s1.length()-s2.length();

    }

    }

    這 個類導(dǎo)出一個帶兩個字符串的方法,它是一個用于字符串比較的具體策略。它是無狀態(tài)的,沒有域,所以,這個類的所有實例在功能上都是等價的,可以節(jié)省不必要 的對象創(chuàng)建開銷。但是我們不好直接把這個類傳遞給可戶使用,因為可戶無法傳遞任何其他的比較策略。相反,我們可以定義一個接口,即我們在設(shè)計具體策略類的 時候還需要定義一個策略接口:

    ????? public interface Comparator{

    ?????????? public int compare(Object o1,Object o2);

    }

    ? 我們完全可以依照自己的需要來定義它。

    具體的策略類往往使用匿名類聲明。

    JAVA中,我們?yōu)榱藢崿F(xiàn)指針的模式,聲明一個接口來表示該策略,并且為每個具體策略聲明一個實現(xiàn)了該接口的類,如果一個具體策略只被使用一次的話,那么通常使用匿名類來聲明和實例化這個具體策略類,如果一個策略類反復(fù)使用,那么它的類通常是一個私有的的靜態(tài)成員類。

    下面我們來討論一下有關(guān)方法設(shè)計的幾個方面,下面說的幾個要點大多數(shù)都是應(yīng)用在構(gòu)造函數(shù)中,當然也使用于普通方法,我們追求的依然是程序的可用性,健壯性和靈活性。

    ?

    Item 23檢查參數(shù)的有效性

    非公有的方法我們應(yīng)該用斷言的方法來檢查它的參數(shù),而不是使用通常大家所熟悉的檢查語句來檢測。如果我們使用的開發(fā)平臺是JDK1.4或者更高級的平臺,我們可以使用assert結(jié)構(gòu);否則我們應(yīng)該使用一種臨時的斷言機制。

    有些參數(shù)在使用過程中是先保存起來,然后在使用的時候再進行調(diào)用,構(gòu)造函數(shù)正是這種類型的一種體現(xiàn),所以我們通常對構(gòu)造函數(shù)參數(shù)的有效性檢查是非常仔細的。

    ?

    Item 24需要時使用保護性拷貝

    眾所周知,JAVA在代碼安全性方面較C/C++有顯著的提高,緩沖區(qū)溢出,數(shù)組越界,非法指針等等,我們的JAVA都有一個很完善的機制來進行免疫,但是這并不代表我們不必去考慮JAVA的安全性,即便在安全的語言,如果不采取措施,還是無法使自己與其他類隔開。假設(shè)類的客戶會盡一切手段來破壞這個類的約束條件,在這樣的前提下,你必須從保護性的方面來考慮設(shè)計程序。通過大量的程序代碼研究我們得出這樣的結(jié)論:對于構(gòu)造性函數(shù)的每個可變參數(shù)進行保護性拷貝是必要的。需要注意的是,保護性拷貝是在檢查參數(shù)的有效性之前進行的,并且有效性檢查是針對拷貝之后的對象,而不是原始的對象。對于“參數(shù)類型可以被不可信方子類化”的情況,不要用clone方法來進行參數(shù)的保護性拷貝。

    對 于參數(shù)的保護性拷貝并不僅僅在于非可變類,當我們編寫一個函數(shù)或者一個構(gòu)造函數(shù)的時候,如果它要接受客戶提供的對象,允許該對象進入到內(nèi)部數(shù)據(jù)結(jié)構(gòu)中,則 有必要考慮一下,客戶提供的對象是否是可變的,如果是,則要考慮其變化的范圍是否在你的程序所能容納的范圍內(nèi),如果不是,則要對對象進行保護性拷貝,并且 讓拷貝之后的對象而不是原始對象進入到數(shù)據(jù)結(jié)構(gòu)中去。當然最好的解決方法是使用非可變的對象作為你的對象內(nèi)部足見,這樣你就可以不必關(guān)心保護性拷貝問題 了。):

    ?

    Item 25謹慎使用設(shè)計方法的原型

    1謹慎的選擇方法的名字:即要注意首先要是易于理解的,其次還要與該包中的其他方法的命名風格相一致,最后當然要注意取一個大眾所認可的名字。

    2) 不要追求提供便利的方法:每一個方法都應(yīng)該提供其應(yīng)具備的功能點,對于接口和類來方法不要過多,否則會對學習使用維護等等方面帶來許多不必要的麻煩,對于 每一個類型所支持的每一個動作,都提供一個功能完全的方法,只有一個方法過于頻繁的使用時,才考慮為它提供一個快捷方法。

    3) 避免過長的參數(shù)列表:通常在實踐中,我們以三個參數(shù)作為最大值,參數(shù)越少越好,類型相同的長參數(shù)列尤其影響客戶的使用,兩個方法可以避免過長的參數(shù)這樣的 情況發(fā)生,一是把一個方法分解成多個,每一個方法只要求使用這些參數(shù)的一個子集;二是創(chuàng)建輔助類,用來保存參數(shù)的聚集,這些輔助類的狀態(tài)通常是靜態(tài)的。

    對于參數(shù)類型,優(yōu)先使用接口而不是類。

    這樣做的目的是避免影響效能的拷貝操作。

    謹慎的使用函數(shù)對象。

    創(chuàng)建函數(shù)對象最容易的方法莫過于使用匿名類,但是那樣會帶來語法上混亂,并且與內(nèi)聯(lián)的控制結(jié)構(gòu)相比,這樣也會導(dǎo)致功能上的局限性。

    ?

    Item 26謹慎的使用重載

    到底是什么造成了重載機制的混淆算法,這是個爭論的話題,一個安全而保守的方法是,永遠不要導(dǎo)出兩個具有相同參數(shù)數(shù)目的重載方法。而對于構(gòu)造函數(shù)來說,一個類的多個構(gòu)造函數(shù)總是重載的,在某些情況下,我們可以選擇靜態(tài)工廠,但是對于構(gòu)造函數(shù)來說這樣做并不總是切合實際的。

    當涉及到構(gòu)造函數(shù)時,遵循這條建議也許是不可能的,但我們應(yīng)該極力避免下面的情形:

    同一組參數(shù)只需要經(jīng)過類型的轉(zhuǎn)換就可以傳遞給不同的重載方法。如果這樣做也不能避免的話,我們至少要保證一點:當傳遞同樣的參數(shù)時,所有的重載方法行為一致。如果不能做到這一點,程序員就不能有效的使用方法或者構(gòu)造函數(shù)。

    ?

    Item 27返回零長度的數(shù)組而不是null

    因為這樣做的原因是編寫客戶程序的程序員可能忘記寫這種專門的代碼來處理null返回值。沒有理由從一個取數(shù)組值的方法中返回null,而不是返回一個零長度數(shù)組。

    ?

    Item 28為所有導(dǎo)出的API元素編寫文檔注釋

    不愛寫注釋可能是大多數(shù)程序員新手的通病(包括偶哈~),但是如果想要一個API真正可用,就必須寫一個文檔來說明它,保持代碼和文檔的同步是一件比較煩瑣的事情,JAVA語言環(huán)境提供了javadoc工具,從而使這個煩瑣的過程變得容易,這個工具可以根據(jù)源代碼自動產(chǎn)生API文檔。

    為了正確得編寫API文檔,我們必須每一個被導(dǎo)出的類,接口,構(gòu)造函數(shù),方法和域聲明之前加一個文檔注釋。

    每一個方法的文檔注釋應(yīng)該見解的描述它和客戶之間的約定。

    我們接下來討論一下Java語言的細節(jié),包括局部變量的處理,庫的使用,以及兩種不是語言本身提供的機制的使用等等一些大家平時可能忽略的問題。

    ?

    Item 29:將局部變量的作用域最小化

    C語言要求局部變量必須被生命在代碼的開始處相比,Java程 序設(shè)計語言寬松得多,它允許你在代碼的任何位置聲明。要想使一個局部變量的作用域最小化,最高小的技術(shù)是在第一次需要使用它的地方聲明,變量的作用域是從 聲明它的地方開始到這個聲明做在的代碼塊的結(jié)束位止,如果我們把變量的聲明和代碼的使用位置分開的過大,那么對于讀這段代碼的人來說,是很不幸的。

    我們幾乎都是在一個局部變量聲明的地方同時給它初始化,注意這是很重要的,甚至有時候,如果我們的初始化應(yīng)該推遲到下一個代碼的位置,我們同時應(yīng)該把聲明也往后延遲。這條規(guī)則唯一的例外是try-catch這個語句,因為如果一個變量被方法初始化,那么這個方法很有可能拋出一個異常,那我們最常用的方法就是把它置于try塊的內(nèi)部去進行初始化。由此我們可以得出,for循環(huán)優(yōu)于while循環(huán),我們在能使用for循環(huán)的地方盡量使用for而不使用while,因為for循環(huán)是完全獨立的,所以重用循環(huán)變量名字不會有任何傷害。

    最后我們要記住的是盡量把我們的函數(shù)寫的小而集中,這樣才能真正組做到最小化局部變量的作用域這一要旨。

    ?

    Item 30:了解和使用庫

    使用標準庫,我們可以充分利用編寫這些庫的Java專家的知識,以及在你之前其他人的使用經(jīng)驗,這就是所謂站在巨人的肩膀上看世界吧~

    在每一個Java平臺的發(fā)行版本里面,都會有許多新的包的加入,和這些更新保持一直是值得的,比如說我們J2ME的開發(fā),在MIDP 1.0的時代,我們要寫個Game還要自己動手寫工具類,現(xiàn)在MIDP2.0推出之后,大多數(shù)寫游戲的人都覺得方便了很多,因為在這個版本里面加入了游戲包,為我們的開發(fā)節(jié)省了大量的人力物力。

    ?

    ???? Item 31:如果想要知道精確的答案,就要避免使用doublefloat

    ???? 對于金融行業(yè)來說,對數(shù)據(jù)的嚴整性要求是很高的,不容半點馬虎,那大家都知道再我們的Java語言里面有兩個浮點數(shù)類型的變量floatdouble,可能大家會認為他們的精度對于金融行業(yè)這樣對數(shù)字敏感的行業(yè)來說,已經(jīng)夠用了,但是在開發(fā)當中,我們要盡量少使用doublefloat,因為讓他們精確的表達0.1是不可能的。那我們?nèi)绾谓鉀Q這個問題呢,答案是使用BigDecimal,int或者long進行貨幣計算。在這里對大家的忠告是:對于商務(wù)運算,我們盡量使用BigDecimal,對于性能要求較高的地方,我們有能力自己處理十進制的小數(shù)點,數(shù)值不太大的時候,我們可以使用int或者long,根據(jù)自己的需要來判定具體使用哪一個,如果范圍超過了18位數(shù),那我們必須使用BigDecimal

    ?

    ??? ?Item 32:如果其他類型更適合,則盡量避免使用字符串

    ???? 在偶看到這條建議之前,我就很喜歡用字符串,不管在什么場合下,先String了再說,但是實際上很多情況下,我們要根據(jù)實際情況來判定到底使用什么類型,而且字符串不適合替代枚舉類型,類型安全枚舉類型和int值 都比字符串更適合用來表示枚舉類型的常量。字符串也不適合替代聚集類型,有一個更好的方法就是簡單的寫一個類來描述這個數(shù)據(jù)集,通常是一個私有的靜態(tài)成員 類最好。字符串也不適合代替能力表,總而言之,如果可以適合更加適合的數(shù)據(jù)類型,或者可以編寫更加適當?shù)臄?shù)據(jù)類型,那么應(yīng)該避免使用字符串來表示對象。

    ?

    Item 33:了解字符串的連接功能

    我們經(jīng)常在使用System.out.println()的時候,往括號里寫一串用“+”連接起來的字符串,這是我們最常見的,但是這個方法并不適合規(guī)模較大的情形,為連接N個字符串而重復(fù)地使用字符串連接操作符,要求N的平方級的時間,這是因為字符串是非可變的,這就導(dǎo)致了在字符串進行連接的時候,前后兩者都要拷貝,這個時候我們就提倡使用StingBuffer替代String

    ?

    Item 34:通過接口引用對象

    通俗的說就是盡量優(yōu)先使用接口而不是類來引用對象,如果有合適的接口存在那么對使用參數(shù),返回值,變量域都應(yīng)該使用接口類型養(yǎng)成使用接口作為對象的習慣,會使程序變得更加靈活。

    如果沒有合適的接口,那么,用類而不是接口來引用一個對象,是完全合適的。

    ?

    Item 35:接口優(yōu)先于映像機制

    java.lang.relect提供了“通過程序來訪問關(guān)于已裝載的類的信息”,由此,我們可以通過一個給定的Class實例,獲得Constructor,MethodField實例。

    映像機制允許一個類使用另一個類,即使當前編譯的時候后者還不存在,但是這種能力也要付出代價:

    我們損失了了編譯時類型檢查的好處,而且要求執(zhí)行映像訪問的代碼非常笨拙和冗長,并且在性能上大大損失。

    通常,普通應(yīng)用在運行時刻不應(yīng)以映像方式訪問對象。

    ?

    Item 36:謹慎的使用本地方法

    JNI允許Java應(yīng)用程序調(diào)用本地方法,所謂本地方法是指用本地程序設(shè)計語言(如CC++)來編寫的特殊方法,本地方法可以在本地語言執(zhí)行任何計算任務(wù),然后返回到Java程序設(shè)計語言中。但是隨著JDK1.3及后續(xù)版本的推出這種通過使用本地方法來提高性能的方法已不值得提倡,因為現(xiàn)在的JVM越來越快了,而且使用本地方法有一些嚴重的缺點,比如使Java原本引以為傲的安全性蕩然無存,總之在使用本地方法的時候要三思。

    ?

    Item 37:謹慎使用優(yōu)化

    不要因為性能而犧牲合理的代碼結(jié)構(gòu),努力編寫好的程序而不是快的程序,但是避免那些限制性能的設(shè)計決定,同時考慮自己設(shè)計的API決定的性能后果,為了獲得更好的性能而對API進行修改這也是一個非常不好的想法,通常我們在做優(yōu)化之后,都應(yīng)該對優(yōu)化的程度進行一些測量。

    ?

    Item 38:遵守普遍接受的命名慣例

    Java有一套比較完善的命名慣例機制,大部分包含在《The Java Language Specification》,嚴格得講這些慣例分成兩類,字面的和語法的。

    字面涉及包,類,接口,方法和域,語法的命名慣例比較靈活,所以爭議更大,字面慣例是非常直接和明確的,而語法慣例則相對復(fù)雜,也很松散。但是有一個公認的做法是:“如果長期養(yǎng)成的習慣用法與此不同的話,請不要盲目遵從

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

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

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

    你 在設(shè)計一個類的時候應(yīng)該盡量的按照4321得順序設(shè)計。如果一個類只是被另一個類使用,那么應(yīng)該考慮把它設(shè)計成這個類的內(nèi)部類。通常public的類不應(yīng) 該有public得字段,不過我們通常會用一個類來定義所有的常量,這是允許的。不過必須保證這些字段要么是基本數(shù)據(jù)類型要么引用指向的對象是不可修改 的。不然他們將可能被修改。例如下面的定義中data就是不合理的,后面兩個沒有問題。
    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)建就不會改變,例如String類。他們的設(shè)計、實現(xiàn)都很方便,安全性高——它們是線程安全的。設(shè)計不可修改類有幾點規(guī)則:

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

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

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

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

    ????為了避免繼承帶來的問題,你必須提供精確的文檔來說明覆蓋相關(guān)方法可能出現(xiàn)的問題。在構(gòu)造器內(nèi)千萬不要調(diào)用可以被覆蓋的方法,因為子類覆蓋方法的時候會出現(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而不是當前的時間。
    由于在Clone()或者序列化的時候非常類似構(gòu)造器的功能,因此readObject()和clone()方法內(nèi)最好也不要包括能被覆蓋的方法。

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

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

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

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

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

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

    ??? 由于以前學過C語言,所以對C還是蠻有感情,而JAVAC又有很多相似之處,很多從C轉(zhuǎn)過來學習JAVA的兄弟,可能一開始都不是很適應(yīng),因為很多在C里面的結(jié)構(gòu)在JAVA里面都不能使用了,所以下面我們來介紹一下C語言結(jié)構(gòu)的替代。

    ?????

    ????? Item 19:用類代替結(jié)構(gòu)

    ????? JAVA剛面世的時候,很多C程序員都認為用類來代替結(jié)構(gòu)現(xiàn)在太復(fù)雜,代價太大了,但是實際上,如果一個JAVA的類退化到只包含一個數(shù)據(jù)域的話,這樣的類與C語言的結(jié)構(gòu)大致是等價的。

    ????? 比方說下面兩個程序片段:

    ????? class Point

    ????? {

    ?????? private float x;

    ?????? private float y;

    ????? }

    ????? 實際上這段代碼和C語言的結(jié)構(gòu)基本上沒什么區(qū)別,但是這段代碼恐怕是眾多OO設(shè)計Fans所不齒的,因為它沒有體現(xiàn)封裝的優(yōu)異性,沒有體現(xiàn)面向?qū)ο笤O(shè)計的優(yōu)點,當一個域被修改的時候,你不可能再采取任何輔助的措施了,那我們再來看一看采用包含私有域和共有訪問方法的OO設(shè)計代碼段:

    ????? class Point

    ????? {

    ?????? private float x;

    ?????? private float y;

    ?????? public Point(float x,float y)

    ?????? {

    ???????????? this.x=x;

    ???????????? this.y=y;

    ?????? }

    ??????? public float getX(){retrun x;}

    ??????? public float getY(){return y;}

    ??????? public void setX(float x){this.x=x;}

    ??????? public void setY(float y){this.y=y;}

    ????? }

    ?

    ??????? 單從表面上看,這段代碼比上面那個多了很多行,還多了很多函數(shù),但是仔細想一下,這樣的OO設(shè)計,似乎更人性化,我們可以方面的對值域進行提取,修改等操作,而不直接和值域發(fā)生關(guān)系,這樣的代碼不僅讓人容易讀懂,而且很安全,還吸取了面向?qū)ο蟪绦蛟O(shè)計的靈活性,試想一下,如果一個共有類暴露它的值域,那么想要在將來的版本中進行修改是impossible的,因為共有類的客戶代碼已經(jīng)遍布各處了。

    需要提醒一點的是,如果一個類是包級私有的,或者是一個私有的嵌套類,則直接暴露其值域并無不妥之處。

    ?

    Item 20用類層次來代替聯(lián)合

    我們在用C語言來進行開發(fā)的時候,經(jīng)常會用到聯(lián)合這個概念,比如:

    ?????? typedef struct{

    ???? double length;

    ???? double width;????

    }rectangleDimensions_t;

    那我們在JAVA里面沒有聯(lián)合這個概念,那我們用什么呢?對!用繼承,這也是JAVA最吸引我的地方之一,它可以使用更好的機制來定義耽擱數(shù)據(jù)類型,在Bruce EckelThinking in java里面也多次提到了一個和形狀有關(guān)的例子,我們可以先籠統(tǒng)的定義一個抽象類,即我們通常所指的超類,每個操作定義一個抽象的方法,其行為取決于標簽的值,如果還有其他的操作不依賴于標簽的值,則把操作變成根類(繼承的類)中的具體方法。

    這樣做的最重要的優(yōu)點是:類層次提供了類型的安全性。

    其次代碼非常明了,這也是OO設(shè)計的優(yōu)點。

    而且它很容易擴展,即使是面向多個方面的工作,能夠同樣勝任。

    最后它可以反映這些類型之間本質(zhì)上的層次關(guān)系,從而允許更強的靈活性,以便編譯時類型檢查。

    ?

    Item 21用類來代替enum結(jié)構(gòu)

    Java程序設(shè)計語言提出了類型安全枚舉的模式來替代enum結(jié)構(gòu),它的基本思想很簡單:定義一個類來代表枚舉類型的單個元素,并且不提供任何公有的構(gòu)造函數(shù),相反,提供公有靜態(tài)final類,使枚舉類型中的每一個常量都對應(yīng)一個域。

    類型安全枚舉類型的一個缺點是,裝載枚舉類的和構(gòu)造常量對象時,需要一定的時間和空間開銷,除非是在資源很受限制的設(shè)備比如蜂窩電哈和烤面包機上,否則在實際中這個問題不會被考慮。

    ?總之,類型安全枚舉類型明顯優(yōu)于int類型,除非實在一個枚舉類型主要被用做一個集合元素,或者主要用在一個資源非常不受限的環(huán)境下,否則類型安全枚舉類型的缺點都不成問題,依次,在要求使用一個枚舉類型的環(huán)境下,我們首先應(yīng)考慮類型安全枚舉類型模式。

    ?

    Item 22用類和接口來代替函數(shù)指針

    眾所周知,JAVA語言和C的最大區(qū)別在于,前者去掉了指針,小生第一次接觸JAVA的時候覺得好不習慣,因為突然一下子沒了指針,覺得好不方面啊,C語言的精髓在于其指針的運用,而JAVA卻把它砍掉了,讓人好生郁悶,不過隨著時間的推移,我漸漸明白了用類和接口的應(yīng)用也同樣可以提供同樣的功能,我們可以直接定義一個這樣一個類,他的方法是執(zhí)行其他方法上的操作,如果一個類僅僅是導(dǎo)出這樣一個方法,那么它實際上就是一個指向該方法的指針,舉個例子:

    ?class StringLengthComprator{

    public int compare(String s1,String s2)

    {

    return s1.length()-s2.length();

    }

    }

    這 個類導(dǎo)出一個帶兩個字符串的方法,它是一個用于字符串比較的具體策略。它是無狀態(tài)的,沒有域,所以,這個類的所有實例在功能上都是等價的,可以節(jié)省不必要 的對象創(chuàng)建開銷。但是我們不好直接把這個類傳遞給可戶使用,因為可戶無法傳遞任何其他的比較策略。相反,我們可以定義一個接口,即我們在設(shè)計具體策略類的 時候還需要定義一個策略接口:

    ????? public interface Comparator{

    ?????????? public int compare(Object o1,Object o2);

    }

    ? 我們完全可以依照自己的需要來定義它。

    具體的策略類往往使用匿名類聲明。

    JAVA中,我們?yōu)榱藢崿F(xiàn)指針的模式,聲明一個接口來表示該策略,并且為每個具體策略聲明一個實現(xiàn)了該接口的類,如果一個具體策略只被使用一次的話,那么通常使用匿名類來聲明和實例化這個具體策略類,如果一個策略類反復(fù)使用,那么它的類通常是一個私有的的靜態(tài)成員類。

    下面我們來討論一下有關(guān)方法設(shè)計的幾個方面,下面說的幾個要點大多數(shù)都是應(yīng)用在構(gòu)造函數(shù)中,當然也使用于普通方法,我們追求的依然是程序的可用性,健壯性和靈活性。

    ?

    Item 23檢查參數(shù)的有效性

    非公有的方法我們應(yīng)該用斷言的方法來檢查它的參數(shù),而不是使用通常大家所熟悉的檢查語句來檢測。如果我們使用的開發(fā)平臺是JDK1.4或者更高級的平臺,我們可以使用assert結(jié)構(gòu);否則我們應(yīng)該使用一種臨時的斷言機制。

    有些參數(shù)在使用過程中是先保存起來,然后在使用的時候再進行調(diào)用,構(gòu)造函數(shù)正是這種類型的一種體現(xiàn),所以我們通常對構(gòu)造函數(shù)參數(shù)的有效性檢查是非常仔細的。

    ?

    Item 24需要時使用保護性拷貝

    眾所周知,JAVA在代碼安全性方面較C/C++有顯著的提高,緩沖區(qū)溢出,數(shù)組越界,非法指針等等,我們的JAVA都有一個很完善的機制來進行免疫,但是這并不代表我們不必去考慮JAVA的安全性,即便在安全的語言,如果不采取措施,還是無法使自己與其他類隔開。假設(shè)類的客戶會盡一切手段來破壞這個類的約束條件,在這樣的前提下,你必須從保護性的方面來考慮設(shè)計程序。通過大量的程序代碼研究我們得出這樣的結(jié)論:對于構(gòu)造性函數(shù)的每個可變參數(shù)進行保護性拷貝是必要的。需要注意的是,保護性拷貝是在檢查參數(shù)的有效性之前進行的,并且有效性檢查是針對拷貝之后的對象,而不是原始的對象。對于“參數(shù)類型可以被不可信方子類化”的情況,不要用clone方法來進行參數(shù)的保護性拷貝。

    對 于參數(shù)的保護性拷貝并不僅僅在于非可變類,當我們編寫一個函數(shù)或者一個構(gòu)造函數(shù)的時候,如果它要接受客戶提供的對象,允許該對象進入到內(nèi)部數(shù)據(jù)結(jié)構(gòu)中,則 有必要考慮一下,客戶提供的對象是否是可變的,如果是,則要考慮其變化的范圍是否在你的程序所能容納的范圍內(nèi),如果不是,則要對對象進行保護性拷貝,并且 讓拷貝之后的對象而不是原始對象進入到數(shù)據(jù)結(jié)構(gòu)中去。當然最好的解決方法是使用非可變的對象作為你的對象內(nèi)部足見,這樣你就可以不必關(guān)心保護性拷貝問題 了。):

    ?

    Item 25謹慎使用設(shè)計方法的原型

    1謹慎的選擇方法的名字:即要注意首先要是易于理解的,其次還要與該包中的其他方法的命名風格相一致,最后當然要注意取一個大眾所認可的名字。

    2) 不要追求提供便利的方法:每一個方法都應(yīng)該提供其應(yīng)具備的功能點,對于接口和類來方法不要過多,否則會對學習使用維護等等方面帶來許多不必要的麻煩,對于 每一個類型所支持的每一個動作,都提供一個功能完全的方法,只有一個方法過于頻繁的使用時,才考慮為它提供一個快捷方法。

    3) 避免過長的參數(shù)列表:通常在實踐中,我們以三個參數(shù)作為最大值,參數(shù)越少越好,類型相同的長參數(shù)列尤其影響客戶的使用,兩個方法可以避免過長的參數(shù)這樣的 情況發(fā)生,一是把一個方法分解成多個,每一個方法只要求使用這些參數(shù)的一個子集;二是創(chuàng)建輔助類,用來保存參數(shù)的聚集,這些輔助類的狀態(tài)通常是靜態(tài)的。

    對于參數(shù)類型,優(yōu)先使用接口而不是類。

    這樣做的目的是避免影響效能的拷貝操作。

    謹慎的使用函數(shù)對象。

    創(chuàng)建函數(shù)對象最容易的方法莫過于使用匿名類,但是那樣會帶來語法上混亂,并且與內(nèi)聯(lián)的控制結(jié)構(gòu)相比,這樣也會導(dǎo)致功能上的局限性。

    ?

    Item 26謹慎的使用重載

    到底是什么造成了重載機制的混淆算法,這是個爭論的話題,一個安全而保守的方法是,永遠不要導(dǎo)出兩個具有相同參數(shù)數(shù)目的重載方法。而對于構(gòu)造函數(shù)來說,一個類的多個構(gòu)造函數(shù)總是重載的,在某些情況下,我們可以選擇靜態(tài)工廠,但是對于構(gòu)造函數(shù)來說這樣做并不總是切合實際的。

    當涉及到構(gòu)造函數(shù)時,遵循這條建議也許是不可能的,但我們應(yīng)該極力避免下面的情形:

    同一組參數(shù)只需要經(jīng)過類型的轉(zhuǎn)換就可以傳遞給不同的重載方法。如果這樣做也不能避免的話,我們至少要保證一點:當傳遞同樣的參數(shù)時,所有的重載方法行為一致。如果不能做到這一點,程序員就不能有效的使用方法或者構(gòu)造函數(shù)。

    ?

    Item 27返回零長度的數(shù)組而不是null

    因為這樣做的原因是編寫客戶程序的程序員可能忘記寫這種專門的代碼來處理null返回值。沒有理由從一個取數(shù)組值的方法中返回null,而不是返回一個零長度數(shù)組。

    ?

    Item 28為所有導(dǎo)出的API元素編寫文檔注釋

    不愛寫注釋可能是大多數(shù)程序員新手的通病(包括偶哈~),但是如果想要一個API真正可用,就必須寫一個文檔來說明它,保持代碼和文檔的同步是一件比較煩瑣的事情,JAVA語言環(huán)境提供了javadoc工具,從而使這個煩瑣的過程變得容易,這個工具可以根據(jù)源代碼自動產(chǎn)生API文檔。

    為了正確得編寫API文檔,我們必須每一個被導(dǎo)出的類,接口,構(gòu)造函數(shù),方法和域聲明之前加一個文檔注釋。

    每一個方法的文檔注釋應(yīng)該見解的描述它和客戶之間的約定。

    我們接下來討論一下Java語言的細節(jié),包括局部變量的處理,庫的使用,以及兩種不是語言本身提供的機制的使用等等一些大家平時可能忽略的問題。

    ?

    Item 29:將局部變量的作用域最小化

    C語言要求局部變量必須被生命在代碼的開始處相比,Java程 序設(shè)計語言寬松得多,它允許你在代碼的任何位置聲明。要想使一個局部變量的作用域最小化,最高小的技術(shù)是在第一次需要使用它的地方聲明,變量的作用域是從 聲明它的地方開始到這個聲明做在的代碼塊的結(jié)束位止,如果我們把變量的聲明和代碼的使用位置分開的過大,那么對于讀這段代碼的人來說,是很不幸的。

    我們幾乎都是在一個局部變量聲明的地方同時給它初始化,注意這是很重要的,甚至有時候,如果我們的初始化應(yīng)該推遲到下一個代碼的位置,我們同時應(yīng)該把聲明也往后延遲。這條規(guī)則唯一的例外是try-catch這個語句,因為如果一個變量被方法初始化,那么這個方法很有可能拋出一個異常,那我們最常用的方法就是把它置于try塊的內(nèi)部去進行初始化。由此我們可以得出,for循環(huán)優(yōu)于while循環(huán),我們在能使用for循環(huán)的地方盡量使用for而不使用while,因為for循環(huán)是完全獨立的,所以重用循環(huán)變量名字不會有任何傷害。

    最后我們要記住的是盡量把我們的函數(shù)寫的小而集中,這樣才能真正組做到最小化局部變量的作用域這一要旨。

    ?

    Item 30:了解和使用庫

    使用標準庫,我們可以充分利用編寫這些庫的Java專家的知識,以及在你之前其他人的使用經(jīng)驗,這就是所謂站在巨人的肩膀上看世界吧~

    在每一個Java平臺的發(fā)行版本里面,都會有許多新的包的加入,和這些更新保持一直是值得的,比如說我們J2ME的開發(fā),在MIDP 1.0的時代,我們要寫個Game還要自己動手寫工具類,現(xiàn)在MIDP2.0推出之后,大多數(shù)寫游戲的人都覺得方便了很多,因為在這個版本里面加入了游戲包,為我們的開發(fā)節(jié)省了大量的人力物力。

    ?

    ???? Item 31:如果想要知道精確的答案,就要避免使用doublefloat

    ???? 對于金融行業(yè)來說,對數(shù)據(jù)的嚴整性要求是很高的,不容半點馬虎,那大家都知道再我們的Java語言里面有兩個浮點數(shù)類型的變量floatdouble,可能大家會認為他們的精度對于金融行業(yè)這樣對數(shù)字敏感的行業(yè)來說,已經(jīng)夠用了,但是在開發(fā)當中,我們要盡量少使用doublefloat,因為讓他們精確的表達0.1是不可能的。那我們?nèi)绾谓鉀Q這個問題呢,答案是使用BigDecimal,int或者long進行貨幣計算。在這里對大家的忠告是:對于商務(wù)運算,我們盡量使用BigDecimal,對于性能要求較高的地方,我們有能力自己處理十進制的小數(shù)點,數(shù)值不太大的時候,我們可以使用int或者long,根據(jù)自己的需要來判定具體使用哪一個,如果范圍超過了18位數(shù),那我們必須使用BigDecimal

    ?

    ??? ?Item 32:如果其他類型更適合,則盡量避免使用字符串

    ???? 在偶看到這條建議之前,我就很喜歡用字符串,不管在什么場合下,先String了再說,但是實際上很多情況下,我們要根據(jù)實際情況來判定到底使用什么類型,而且字符串不適合替代枚舉類型,類型安全枚舉類型和int值 都比字符串更適合用來表示枚舉類型的常量。字符串也不適合替代聚集類型,有一個更好的方法就是簡單的寫一個類來描述這個數(shù)據(jù)集,通常是一個私有的靜態(tài)成員 類最好。字符串也不適合代替能力表,總而言之,如果可以適合更加適合的數(shù)據(jù)類型,或者可以編寫更加適當?shù)臄?shù)據(jù)類型,那么應(yīng)該避免使用字符串來表示對象。

    ?

    Item 33:了解字符串的連接功能

    我們經(jīng)常在使用System.out.println()的時候,往括號里寫一串用“+”連接起來的字符串,這是我們最常見的,但是這個方法并不適合規(guī)模較大的情形,為連接N個字符串而重復(fù)地使用字符串連接操作符,要求N的平方級的時間,這是因為字符串是非可變的,這就導(dǎo)致了在字符串進行連接的時候,前后兩者都要拷貝,這個時候我們就提倡使用StingBuffer替代String

    ?

    Item 34:通過接口引用對象

    通俗的說就是盡量優(yōu)先使用接口而不是類來引用對象,如果有合適的接口存在那么對使用參數(shù),返回值,變量域都應(yīng)該使用接口類型養(yǎng)成使用接口作為對象的習慣,會使程序變得更加靈活。

    如果沒有合適的接口,那么,用類而不是接口來引用一個對象,是完全合適的。

    ?

    Item 35:接口優(yōu)先于映像機制

    java.lang.relect提供了“通過程序來訪問關(guān)于已裝載的類的信息”,由此,我們可以通過一個給定的Class實例,獲得Constructor,MethodField實例。

    映像機制允許一個類使用另一個類,即使當前編譯的時候后者還不存在,但是這種能力也要付出代價:

    我們損失了了編譯時類型檢查的好處,而且要求執(zhí)行映像訪問的代碼非常笨拙和冗長,并且在性能上大大損失。

    通常,普通應(yīng)用在運行時刻不應(yīng)以映像方式訪問對象。

    ?

    Item 36:謹慎的使用本地方法

    JNI允許Java應(yīng)用程序調(diào)用本地方法,所謂本地方法是指用本地程序設(shè)計語言(如CC++)來編寫的特殊方法,本地方法可以在本地語言執(zhí)行任何計算任務(wù),然后返回到Java程序設(shè)計語言中。但是隨著JDK1.3及后續(xù)版本的推出這種通過使用本地方法來提高性能的方法已不值得提倡,因為現(xiàn)在的JVM越來越快了,而且使用本地方法有一些嚴重的缺點,比如使Java原本引以為傲的安全性蕩然無存,總之在使用本地方法的時候要三思。

    ?

    Item 37:謹慎使用優(yōu)化

    不要因為性能而犧牲合理的代碼結(jié)構(gòu),努力編寫好的程序而不是快的程序,但是避免那些限制性能的設(shè)計決定,同時考慮自己設(shè)計的API決定的性能后果,為了獲得更好的性能而對API進行修改這也是一個非常不好的想法,通常我們在做優(yōu)化之后,都應(yīng)該對優(yōu)化的程度進行一些測量。

    ?

    Item 38:遵守普遍接受的命名慣例

    Java有一套比較完善的命名慣例機制,大部分包含在《The Java Language Specification》,嚴格得講這些慣例分成兩類,字面的和語法的。

    字面涉及包,類,接口,方法和域,語法的命名慣例比較靈活,所以爭議更大,字面慣例是非常直接和明確的,而語法慣例則相對復(fù)雜,也很松散。但是有一個公認的做法是:“如果長期養(yǎng)成的習慣用法與此不同的話,請不要盲目遵從

    posted on 2006-09-11 18:14 Alex 閱讀(694) 評論(0)  編輯  收藏 所屬分類: javadesign
    主站蜘蛛池模板: 亚洲AV无码一区二区乱子伦| 亚洲熟妇av一区| 亚洲va成无码人在线观看| 国产91成人精品亚洲精品| 四虎影视成人永久免费观看视频| 免费a级毛片无码a∨蜜芽试看| 国产av无码专区亚洲国产精品| 亚洲国产高清美女在线观看| 有码人妻在线免费看片| 一个人看的www在线观看免费| 久久亚洲国产成人精品无码区| 亚洲区精品久久一区二区三区| 一级女人18片毛片免费视频| 日韩视频在线精品视频免费观看| 美腿丝袜亚洲综合| 亚洲一区二区观看播放| 无码国产精品一区二区免费16| 亚洲 自拍 另类小说综合图区| 亚洲乱码一区av春药高潮| 99视频在线免费观看| 在线观看亚洲免费视频| 666精品国产精品亚洲| 国产三级在线免费观看| 国产男女猛烈无遮挡免费视频网站 | 亚洲AV无码一区二区乱子伦| 精品国产_亚洲人成在线| 国产91色综合久久免费分享| 亚洲精品国产字幕久久不卡| 狠狠入ady亚洲精品| 成人免费一级毛片在线播放视频| 亚洲国产精品无码专区| 最好2018中文免费视频| 97人伦色伦成人免费视频| 久久亚洲精品中文字幕| 久久久WWW免费人成精品| 四虎影视永久免费观看| 亚洲视频在线观看2018| 在线免费观看国产| 亚洲va中文字幕无码久久| 一级毛片免费播放视频 | 日韩免费视频播播|