欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Java高效編程
  • 創(chuàng )建及銷(xiāo)毀對象
    • - 考慮用靜態(tài)工廠(chǎng)方法替代構造函數
      • 靜態(tài)工廠(chǎng)方法的優(yōu)勢
        • 靜態(tài)工廠(chǎng)方法的一個(gè)優(yōu)勢是,它們具有自己的名字。構造函數的參數自身無(wú)法描述被返回的對象,而選用名字合適的靜態(tài)工廠(chǎng)方法可以使類(lèi)的使用更加容易,產(chǎn)生的客戶(hù)代碼更容易閱讀。
        • 靜態(tài)工廠(chǎng)方法的第二個(gè)優(yōu)勢是它們不需要在每次調用時(shí)都去創(chuàng )建一個(gè)新的對象。這使得非可變類(lèi)可以使用預先構造的實(shí)例,或者在構造階段先緩存這些實(shí)例,然后重復使用它們,從而避免創(chuàng )建不必要的重復對象。
        • 靜態(tài)工廠(chǎng)方法為重復調用而返回同一對象的能力,可以用來(lái)控制某一時(shí)刻實(shí)例的存在情況。
          • 有兩個(gè)理由使靜態(tài)方法可以做到這一點(diǎn)。
            • 首先它能夠使類(lèi)保證實(shí)例是singleton。
            • 其次,它能夠使非可變類(lèi)保證沒(méi)有兩個(gè)相等的實(shí)例同時(shí)存在。
        • 靜態(tài)工廠(chǎng)的第三個(gè)優(yōu)勢是它們可以返回到返回類(lèi)型的任何子類(lèi)型(subtype)對象。這使用戶(hù)在選擇返回對象的類(lèi)時(shí)具有很大的靈活性。
      • 靜態(tài)工廠(chǎng)的缺陷
        • 靜態(tài)工廠(chǎng)的主要缺陷是類(lèi)在沒(méi)有公共或受保護的構造函數時(shí)不能被子類(lèi)化。例如,不可能子類(lèi)化Collections Framework中的任何集合類(lèi)。
        • 靜態(tài)工廠(chǎng)方法的另一個(gè)缺陷是名字之間不容易區分。
      • 總地來(lái)說(shuō),如果你權衡過(guò)了這兩種方法,同時(shí)沒(méi)有其他因素影響你的選擇取向,那么最好還是簡(jiǎn)單地使用構造函數,因為這符合規范。
    • - 使用私有構造函數強化singleton屬性
      • singleton類(lèi)就是一種只能被實(shí)例化一次的簡(jiǎn)單類(lèi)。
      • - 實(shí)現singlton有兩種途徑,這兩種途徑都以保持構造函數私有及提供公共的靜態(tài)成員允許客戶(hù)訪(fǎng)問(wèn)它的唯一實(shí)例為基礎。
        • 一種實(shí)現方法是,公共的靜態(tài)成員是一個(gè)final域:
          //singleton with final field
          public class Elvis {
                    public static final Elvis INSTANCE = new Elvis();  
                    private Elvis() {
                               ...
                    }  
                    ...
          }
          私有構造函數只被調用一次,以初始化公共的靜態(tài)final域Elvis.INSTANCE。公共的或受保護的構造函數的缺乏,保證了一個(gè)“唯一的elvis”的世界:一旦Elvis類(lèi)被初始化,僅有一個(gè)Elvis實(shí)例存在。

          第一種方式的突出優(yōu)點(diǎn)是類(lèi)的成員的聲明使類(lèi)清楚地表明它是singleton:公共的靜態(tài)域是final,所以這個(gè)域將總是包含著(zhù)相同的對象引用。

          第二種方法中,提供了公共的靜態(tài)工廠(chǎng)方法,取代了公共的靜態(tài)final域:
          //singleton with static factory
          public class Elvis {
                    private static final Elvis INSTANCE = new Elvis();  
                    private Elvis() {
                               ...
                    }
                    public static Elvis getInstance() {
                               return INSTANCE;
                    }
          }
          對所有的靜態(tài)方法Elvis.getInstance的調用,都返回同一個(gè)對象的引用,沒(méi)有其他的Elvis實(shí)例被創(chuàng )建。

          第二種方式的突出優(yōu)點(diǎn)在于它給使用者提供了靈活性,當你決定把這個(gè)類(lèi)改變?yōu)榉莝ingleton的時(shí)候,無(wú)需修改API。

          總而言之,如果你確信該類(lèi)將永遠是singleton的,那么應該使用第一種方法。反之,第二種方式是更好的選擇。

    • - 用私有構造函數強化不可實(shí)例化能力
      • 有些類(lèi)不希望被實(shí)例化,對它們實(shí)例化也沒(méi)有意義。

        試圖通過(guò)將類(lèi)抽象化來(lái)強化類(lèi)的不可實(shí)例化能力是行不通的。這是因為類(lèi)可以被子類(lèi)化,而子類(lèi)可以被實(shí)例化。這種做法還會(huì )誤導用戶(hù),以為這種類(lèi)的目的是為了實(shí)現繼承的。

        有一種簡(jiǎn)單的方法可以解決這一問(wèn)題。由于缺省的構造函數僅在類(lèi)不包含顯示的構造函數時(shí)才會(huì )生成,我們可以在類(lèi)中包含顯式的私有類(lèi)型構造函數來(lái)實(shí)現類(lèi)的不可實(shí)例化特性。

        因為聲明的構造函數是私有的,所以它在類(lèi)的外部不可訪(fǎng)問(wèn)。假設構造函數不會(huì )被類(lèi)自身從內部調用,即能保證類(lèi)永遠不會(huì )被實(shí)例化。 例:
        class F {
               private F() {
                       ...
               }
               ...
        }

    • - 避免創(chuàng )建重復對象
      • 重用同一對象比每次都創(chuàng )建功能等同的新對象通常更適合。重用方式既快速也更加時(shí)尚。

        下面的語(yǔ)句是個(gè)極端的例子,千萬(wàn)不要這樣做:
        String s = new String("Silly"); //never do this
        該語(yǔ)句每次執行時(shí)都創(chuàng )建一個(gè)新的String實(shí)例,然而這些對象的創(chuàng )建是不必要的。

        一個(gè)簡(jiǎn)單的版本如下: String s = "No longer silly";

        除了對非可變對象重用,我們也可以重用可變(mutable)對象,只要知道它不會(huì )發(fā)生變化。下面是一個(gè)例子,不要使用這種做法:
        public class Person {
               private final Date birthDate;
               public Person(Date birthDate) {
                       this.birthDate = birthDate;
               }

               //Don‘t do this!
               public boolean isBabyBoomer() {
                       Calendar gmtCal = Calender.getInstance(Timezone.getTimezone("GMT"));
                       gmtCal.set(1946, Calender.JANUARY, 1, 0, 0, 0);
                       Date boomStart = gmtCal.getTime();
                       gmtCal.set(1965, Calender.JANUARY, 1, 0, 0, 0);
                       Date boomEnd = gmtCal.getTime();
                       return birthDate.compareTo(boomStart) >= 0
                                   && birthDate.compareTo(boomEnd) <0;
               }
        }
        在每次被調用時(shí),方法isBabyBoomer都不必要地創(chuàng )建了一個(gè)新的Calendar、TimeZone和兩個(gè)Date實(shí)例。

        下面的版本通過(guò)使用靜態(tài)方法的初始化方法避免了這樣的低效做法。
        public class Person {
               private final Date birthDate;
               public Person(Date birthDate) {
                       this.birthDate = birthDate;
               }
               private static final Date BOOM_START;
               private static final Date BOOM_END;  

               static {
                       Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
                       gmtCal.set(1946, Calender.JANUARY, 1, 0, 0, 0);
                       Date BOOM_START = gmtCal.getTime();
                       gmtCal.set(1965, Calender.JANUARY, 1, 0, 0, 0);
                       Date BOOM_END = gmtCal.getTime();
               }

               public boolean isBabyBoomer() {
                       return birthDate.compareTo(boomStart) >= 0
                                       && birthDate.compareTo(boomEnd) <0;
               }
        }

    • - 消除對過(guò)期對象的引用
      • 例: //Can you spot the "memory leak"?
        public class Stack {
               private Object[] elements;
               private int size = 0;
               public Stack(int initialCapacity) {
                       this.elements = new Object[initialCapacity];
               }  

               public void push(Object e) {
                       ensureCapacity();
                       elements[size++] = e;
               }

               public Object pop() {
                       if(size =- 0)
                               throw new EmptyStackException();
                       return elements[--size];
               }  

               private void ensureCapacity() {
                       if(elements.length == size) {
                               Object[] OldElements = elements;                              elements = new Object[2*elements.lenght+1];                System.arraycopy(OldElements,0,elements,0,size);
                       }
               }
        }

        這個(gè)程序沒(méi)有明顯的錯誤,但是卻隱藏著(zhù)一個(gè)問(wèn)題,不嚴格地講,程序有一個(gè)“內存漏洞”。如果棧先增長(cháng)再收縮,那么被棧彈出的對象即使在程序不再引用它們時(shí),也不會(huì )被垃圾回收單元回收。這是由于棧維護著(zhù)對這些對象的過(guò)期引用(obsolete reference)。所謂過(guò)期引用,是指永遠不會(huì )被解除引用的引用。

        解決這種問(wèn)題的方法很簡(jiǎn)單:一旦它們過(guò)期,清除掉對它們的引用。
        pop正確的版本是:
        public Object pop() {
                  if(size == 0)
                             throw new EmptyStackException();
                  Object result = elements[--size];
                  elements[size] = null;
                  return result;
        }

        清除掉過(guò)期引用的額外好處是,如果它們隨后被錯誤地解除引用,程序將會(huì )由于NullPointerException異常退出,而不是繼續錯誤地運行下去。

    • - 避免使用終結程序
      • 終結程序(finalizer)的行為是不可預測的,而且是危險的,通常也不必要。不要把終結程序作為C++析構函數(destructor)的類(lèi)似物。

        終結程序無(wú)法保證能別及時(shí)的執行,這意味著(zhù)不能用終結程序來(lái)處理時(shí)間關(guān)鍵(time-critical)性的操作。例如,依賴(lài)終結程序去關(guān)閉打開(kāi)的文件是一個(gè)嚴重的錯誤,因為打開(kāi)的文件描述符是一種有限資源,而JVM不會(huì )及時(shí)地安排終結程序執行,如果多個(gè)文件處于打開(kāi)狀態(tài),程序有可能會(huì )因為無(wú)法再打開(kāi)文件而執行失敗。

        JLS不僅不保證終結程序的及時(shí)執行,它甚至不保證終結程序會(huì )獲得執行。永遠不要依賴(lài)終結程序去更新關(guān)鍵的持續狀態(tài)(persistent state)。例如,依靠終結程序釋放一個(gè)共享資源如數據庫上的持續鎖,將是導致所有分布系統跨掉的絕佳方法。

  • 類(lèi)和接口
    • - 最小化類(lèi)和成員的可訪(fǎng)問(wèn)能力
      • 區分設計良好與設計拙劣的模塊的唯一也是最重要的元素是模塊向外部模塊隱藏內部數據和其他實(shí)現細節的程度。
      • 經(jīng)驗規則指出應該使每個(gè)類(lèi)或成員盡可能地不被外部訪(fǎng)問(wèn)。換句話(huà)說(shuō),在設計程序時(shí),應該根據軟件功能,使用允許的最低的訪(fǎng)問(wèn)等級。
      • 有一條規則限制了我們給方法降低可訪(fǎng)問(wèn)性的能力:如果方法要重載超類(lèi)中的方法,那么不允許子類(lèi)中該方法的訪(fǎng)問(wèn)等級低于超類(lèi)中的訪(fǎng)問(wèn)等級。
    • - 傾向于非可變性
      • 非可變類(lèi)就是類(lèi)的實(shí)例不能被修改的類(lèi)。如String。

        非可變類(lèi)有很多好的存在理由

        • 非可變類(lèi)更容易設計、實(shí)現和使用
        • 不易犯錯誤
        • 更安全

        為了使類(lèi)成為非可變的,要遵循下面5條原則

        • 不要提供任何修改對象的方法。
        • 保證沒(méi)有可以被重載的方法。這防止了粗心的或惡意的子類(lèi)危害對象的不可變行為。防止方法被重載的一般方式是是類(lèi)成為final的。
        • 使所有的域成為final。
        • 使所有的域都是私有的。
        • 保證對任何可變組件互斥訪(fǎng)問(wèn)。

        下面是一個(gè)稍微復雜的例子:
        public final class Complex {
               private final float re;
               private final float im;  
               public Complex(float re, float im) {
                       this.re = re;
                       this.im = im;
               }
               public float realPart()        { return re; }
               public float imaginaryPart()        { return im; }  

               public Complex add(Complex c) {
                       return new Complex(re+c.re,im+c.im);
               }  
              
                ...  
               
               public boolean equals(Object o) {
                       if(o==this)
                               return true;
                       if(!(o instanceOf Complex))
                               return false;  
                       Complex c = (Complex) 0;
                       
                       return(Float.floatToIntBits(re) == Float.floatToIntBits(c.re) ) &&
                                       (Float.floatToIntBits(im) == Float.floatToIntBits(c.im));
               }  
               
               public int hashCode() {
                       int result = 17 + Float.floatToIntBits(re);
                       result = 37*result + Float.floatToIntBits(im);
                       return result;
               }  
               
               public String toString() {
                       return "("+re+" + "+im+"i)";
                }
        }
        這個(gè)類(lèi)表示復數,注意到算術(shù)操作創(chuàng )建和返回一個(gè)新的復數實(shí)例,而不是修改了這個(gè)實(shí)例。

        非可變對象本質(zhì)上是線(xiàn)程安全的,不需要同步機制。這是獲得線(xiàn)程安全最簡(jiǎn)單的途徑。線(xiàn)程不會(huì )看到其他線(xiàn)程對非可變對象施加的影響,因而,非可變對象可以被自由地共享。

        非可變對象可以提供一個(gè)靜態(tài)工廠(chǎng),將經(jīng)常被請求的實(shí)例緩存起來(lái),避免在被請求的實(shí)例存在時(shí),重復地創(chuàng )建新的實(shí)例。BigInteger和Boolean類(lèi)都具有這種靜態(tài)工廠(chǎng)方法。

        不僅可以共享非可變對象,還可以共享它們的內部信息。

        非可變對象為其他對象——無(wú)論是可變還是非可變的,創(chuàng )建了大量的構造塊。

        非可變對象的真正也是唯一的缺點(diǎn)是對每個(gè)不同的值要求一個(gè)單獨的對象。

    • - 組合優(yōu)于繼承
      • 繼承是實(shí)現代碼重用的有力途徑,但它不總是完成這項工作的最后的工具。與方法調用不同,繼承打破了封裝性。子類(lèi)的特有的功能,依賴(lài)于它的超類(lèi)的實(shí)現細節。超類(lèi)的實(shí)現會(huì )隨著(zhù)版本而改變,如果出現這樣的情況,即使不觸動(dòng)子類(lèi)的代碼,它也會(huì )被破壞。

        不去擴展現有的類(lèi),而是給類(lèi)增加一個(gè)引用現有類(lèi)實(shí)例的新的私有域,這種設計方法被成為復合(composition),因為現有的類(lèi)成為了新的類(lèi)的一部分。

        繼承只有當子類(lèi)確實(shí)是超類(lèi)的“子類(lèi)型”(subtype)時(shí),才是適合的。換句話(huà)說(shuō),對兩個(gè)類(lèi)A和B,如果“B是A”的關(guān)系存在,那么B應該擴展A。在把B擴展A時(shí),問(wèn)這樣一個(gè)問(wèn)題:“任何的B都是A嗎?”,如果答案是否定的,那么通常應該把A作為B的一個(gè)私有實(shí)例,然后暴露更小、更簡(jiǎn)單的API:A不是B的基本部分,只是它的實(shí)現細節。

    • - 設計和文檔化繼承
      • 類(lèi)必須提供文檔準確地描述重載任一方法的效果。類(lèi)必須說(shuō)明它的可重載方法的自用性(self-use):對每個(gè)公共的或受保護的方法或構造函數,它的文檔信息都必須要表明它在調用哪一個(gè)可重載的方法、以什么順序調用及每一個(gè)調用的結果如何影響后面的處理。

        為了允許程序員有效地進(jìn)行子類(lèi)化處理而不必承受不必要的痛苦,類(lèi)必須用認真選擇的受保護方法提供它內部實(shí)現的鉤子(hook)。

        構造函數一定不能調用可重載的方法,無(wú)法直接地還是間接地。超類(lèi)構造函數會(huì )在子類(lèi)構造函數之前運行,所以子類(lèi)中的重載方法會(huì )在子類(lèi)構造函數運行之前被調用。如果重載方法依賴(lài)于由子類(lèi)構造函數執行的初始化,那么該方法將不會(huì )按期望的方式執行。

        例:
        public class Super { //Broken - constructor invokes overridable method
               public Super(){
                       m();
               }  
               public void m() {     
               }
        }  
        下面的子類(lèi)重載了m,而m被超類(lèi)唯一的構造函數錯誤地調用了:
        final class sub extends Super {
               private final Date date;  
               //Blank final, set by constructor
               Sub() {
                       date = new Date();
               }  
               //Overrides Super.m, invoked by the constructor Super()
               public void m() {
                       System.out.println(date);
               }  
               public static void main(String[] args) {
                       Sub s = new Sub();
                       s.m();
               }  
        }  
        它第一次打印出的是null。這是因為方法m被構造函數Super()在構造函數Sub()初始化date域之前被調用。

        clone和readObject方法都不能調用可重載的方法,無(wú)論是直接的還是間接的。如果確定要用來(lái)繼承的類(lèi)實(shí)現Serializable,并且類(lèi)中有一個(gè)readResolve或writeReplace方法,那么必須使readResolve或writeReplace方法成為受保護的而不是私有的。一旦這些方法是私有的,它們就將被子類(lèi)悄然忽略。

        對那些不是專(zhuān)門(mén)設計用于安全地實(shí)現子類(lèi)化并具有文檔說(shuō)明的類(lèi),禁止子類(lèi)化。

        禁止子類(lèi)化的方法有兩種

        • 最容易的方法是將類(lèi)聲明為final的。
        • 另一種是使所有的構造函數私有或者成為包內私有,并用公共的靜態(tài)工廠(chǎng)取代構造函數。
    • - 接口優(yōu)于抽象類(lèi)
      • Java語(yǔ)言為定義允許有多種實(shí)現的類(lèi)型提供了兩種機制:接口和抽象類(lèi)。

        • 兩種機制的最明顯區別是抽象類(lèi)允許包含某種方法的實(shí)現,而接口不允許。
        • 一個(gè)更重要的區別:為實(shí)現有抽象類(lèi)定義的類(lèi)型,實(shí)現的類(lèi)必須是抽象類(lèi)的子類(lèi)。Java只允許單一繼承,因此抽象類(lèi)作為類(lèi)型的定義受到了極大的限制。

        現有的類(lèi)可以很容易的被更新以實(shí)現新的接口。所有需要做的工作是增加還不存在的方法并在類(lèi)的聲明中增加一個(gè)implement語(yǔ)句。

        接口是定義混合類(lèi)型(mixins)的理想選擇。mixin是這樣的類(lèi)型:除了它的“基本類(lèi)型(primary type)”外,類(lèi)還可以實(shí)現額外的類(lèi)型,以表明它提供了某些可選的功能。

        接口允許非層次類(lèi)型框架的構造。對于組織某些事物,類(lèi)型層次是極好的選擇,但有些事物不能清楚地組織成嚴格的層次。

        接口通過(guò)使用封裝類(lèi)方式,能夠獲得安全、強大的功能。如果使用抽象類(lèi)定義類(lèi)型,那么程序員在增加功能是,除了使用繼承外別無(wú)選擇,而且得到的類(lèi)與封裝類(lèi)相比功能更差、更脆弱。

        盡管接口不允許方法實(shí)現,但使用接口定義類(lèi)型并不妨礙給程序員提供實(shí)現上的幫助??梢酝ㄟ^(guò)抽象的構架實(shí)現類(lèi)與希望輸出的所有重要的接口配合,從而將接口與抽象類(lèi)的優(yōu)點(diǎn)組合在一起。

        使用抽象類(lèi)定義具有多個(gè)實(shí)現的類(lèi)型與使用接口相比有一個(gè)明顯優(yōu)勢:演進(jìn)抽象類(lèi)比演進(jìn)接口更容易。如果在以后的版本重,需要給抽象類(lèi)增加新方法,那么總可以增加一個(gè)包含正確的缺省實(shí)現的具體方法。此時(shí)所有現存的該抽象類(lèi)的實(shí)現都會(huì )具有這個(gè)新的方法。

    • - 靜態(tài)成員優(yōu)于非靜態(tài)的
      • 嵌套類(lèi)(nested class)是一種定義在其他類(lèi)內部的類(lèi)。嵌套類(lèi)應該僅僅為包容它的類(lèi)而存在。

        - 有四種類(lèi)型嵌套類(lèi):

        • 靜態(tài)成員類(lèi)(static member class)
          • 靜態(tài)成員類(lèi)是最簡(jiǎn)單的嵌套類(lèi)。它最好被看做是普通的類(lèi)碰巧被聲明在其他的類(lèi)的內部。它對所有封閉類(lèi)中的成員有訪(fǎng)問(wèn)權限,甚至那些私有成員。靜態(tài)成員類(lèi)的一種通常用法是作為公共的輔助類(lèi),僅當和它的外部類(lèi)協(xié)作時(shí)才有意義。

            如果聲明了一個(gè)不要求訪(fǎng)問(wèn)封閉實(shí)例的成員類(lèi),切記要在它的聲明里使用static修飾符,把成員類(lèi)變?yōu)殪o態(tài)的。 私有靜態(tài)成員類(lèi)的一般用法是用來(lái)表示它們的封閉類(lèi)對象的組件。

        • 非靜態(tài)成員類(lèi)(nonstatic member classer)
          • 每一個(gè)非靜態(tài)的成員類(lèi)與它包含類(lèi)的封閉實(shí)例(enclosing instance)隱式地關(guān)聯(lián)。如果嵌套類(lèi)的實(shí)例可以在它的封閉類(lèi)實(shí)例之外單獨存在,那么嵌套類(lèi)不能成為非靜態(tài)成員類(lèi):創(chuàng )建沒(méi)有封閉實(shí)例的非靜態(tài)成員類(lèi)實(shí)例是不可能的。非靜態(tài)成員實(shí)例與它的封閉實(shí)例之間的關(guān)聯(lián)在前者被創(chuàng )建時(shí)即建立,在此之后它不能被修改。

        • 匿名類(lèi)(anonymous class)
          • 匿名類(lèi)的行為與靜態(tài)的或非靜態(tài)的成員類(lèi)一樣,依賴(lài)于它們出現的位置:如果它們出現在非靜態(tài)的上下文中,則具有封閉實(shí)例。匿名類(lèi)僅在代碼中唯一的一點(diǎn)被實(shí)例化才能被使用,由于匿名類(lèi)沒(méi)有名字,因此僅在被實(shí)例化后不需要再被訪(fǎng)問(wèn)的情況下才適合使用。

            過(guò)長(cháng)的匿名類(lèi)會(huì )傷害程序的可讀性。
            //Typical use of an anonymous class
            Arrays.sort(args, new Comparator() {
                          public int compare(Object o1,Object o2) {
                                         return ((String)o1).length() - ((String)o2).length();
                          }
            }

            匿名類(lèi)另一種通常用法是創(chuàng )建過(guò)程對象(process object),例如Thread、Runnable或者TimerTask實(shí)例。

            第三種常用法是在靜態(tài)工廠(chǎng)方法內使用。

            第四種用法用在復雜的類(lèi)型安全枚舉類(lèi)型——要求給每個(gè)實(shí)例提供單獨的子類(lèi)——的公共靜態(tài)的final域初始化程序中。

        • 局部類(lèi)(local class)
          • 在局部變量可以聲明的地方都可以聲明局部類(lèi),它們同樣遵守作用域規則,性質(zhì)跟匿名類(lèi)一樣。
        • 除了第一種,其他的三種類(lèi)都被稱(chēng)為內部類(lèi)(inner class)

        四種嵌套類(lèi),每種都有自己的用處。如果嵌套類(lèi)在單一的方法之外可見(jiàn),或是太長(cháng)而不合適使用在一個(gè)方法內,那么使用成員類(lèi)。如果成員類(lèi)實(shí)例需要它的封閉類(lèi)的引用,那么使它成為非靜態(tài)的;否則為靜態(tài)的。如果類(lèi)存在于方法內部,那么如果你僅需要在唯一一個(gè)位置創(chuàng )建實(shí)例,并且已存在刻化該類(lèi)的類(lèi)型,則使它成為匿名類(lèi);否則,用局部類(lèi)。

  • 對象的通用方法
    • - 重載equals時(shí)要遵守通用約定
      • - 如果滿(mǎn)足下列條件,就不要重載equals
        • 每個(gè)類(lèi)實(shí)例本質(zhì)上是唯一的。

          不關(guān)心類(lèi)是否提供了“邏輯意義的等同”(logical equality)測試。例如java.util.Random本來(lái)可以重載equals方法,用以檢查兩個(gè)Random實(shí)例是否會(huì )產(chǎn)生相同的隨機數序列,但設計者不認為客戶(hù)會(huì )需要或想要這個(gè)功能。這種情況下,使用從Object繼承的equals實(shí)現就夠了。

          超類(lèi)已經(jīng)重載了equals,而從超類(lèi)繼承的行為適合該類(lèi)。

          類(lèi)是私有的或包內私有(package-private)的,而且可以確定它的equals方法永遠不會(huì )被調用。

      • 當類(lèi)有邏輯上的等同意義而不僅僅是對象意義上的等同,而且超類(lèi)沒(méi)有重載equals方法以實(shí)現期望的行為,這時(shí)才需要重載。
      • - equals方法實(shí)現了相等關(guān)系(equivalence relation)
        • 自反性(reflective):對于任意的引用值x,x.equals(x)總是返回true。
        • 對稱(chēng)性(symmetric):對于任意的引用值x、y,如果y.equals(x)返回true,x.equals(y)總返回true。
        • 傳遞性(transitive):對于任意的引用值x、y、z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)總是返回true.
        • 一致性(consistent):對于任意的引用值x、y,如果對象中用于equals比較的信息沒(méi)有修改,那么對x.equals(y)的多個(gè)調用,要么一致為true,要么一致為false。
        • 對于任何非空引用值x,x.equals(null)總是返回false。
      • - 為實(shí)現高質(zhì)量的equals方法,下面提供一些方法
        • 用"=="操作符檢查是否參數是對該對象的引用。

          用instanceof操作符檢查是否參數是正確的類(lèi)型。
          public boolean equals(Object o) {
                  if(!(o instanceof SomeClass))
                           return false;
                  ...
          }

          把參數映射到正確的類(lèi)型。

          對類(lèi)中每一個(gè)“主要的”(significant)域,檢查是否參數中的域與對象中的相應的域匹配。

          完成equals方法時(shí),問(wèn)自己3個(gè)問(wèn)題:它是否是對稱(chēng)的、傳遞的、一致的。

      • - 實(shí)現equals方法應該注意的地方
        • 在重載equal方法時(shí)要重載hashCode方法。

          不要使自己聰明過(guò)頭。把任何的同義形式考慮在比較的范圍內一般是糟糕的想法,例如File類(lèi)不應該與指向同一文件的符號鏈接進(jìn)行比較,實(shí)際上File類(lèi)也沒(méi)有這樣做。

          不要設計依賴(lài)于不可靠資源的equals方法。

          不要將equals聲明中的Object替換為其他類(lèi)型。程序員編寫(xiě)出形如下面所示的equals方法并不少見(jiàn),它會(huì )讓人摸不清頭腦:所設計方法為什么不能正確工作:
          public boolean equals(Myclass o) {
                 ...
          }
          問(wèn)題出在這個(gè)方法沒(méi)有重載(override)參數為Object類(lèi)型的Object.equals方法。而是過(guò)載(overload)了它。這在正常的equals方法中,又提供了一個(gè)“強類(lèi)型”的equals方法。

    • - 重載equals時(shí)永遠要重載hashCode
      • 一定要在每一個(gè)重載了equals的類(lèi)中重載hashCode方法。不這樣做會(huì )違背Object.hashCode的一般約定,并導致你的類(lèi)與所有基于散列的集合一起作用時(shí)不能正常工作,這些集合包括HashMap、HashSet和Hashtable。

        不重載hashCode方法違背了java.lang.Object的規范:相等的對象必須有相等的散列碼。兩個(gè)截然不同的實(shí)例根據類(lèi)的equals方法也許邏輯上是等同的,但對于Object類(lèi)的hashCode方法,它們就是兩個(gè)對象,僅此而已。因而對象的hashCode方法返回兩個(gè)看上去是隨機的數值,而不是約定中要求的相等的值。

        好的hash函數傾向于為不相等的對象生成不相等的hash碼。理想的情況下,hash函數應該把所有不相等的實(shí)例的合理集合均一地分布到所有可能的hash值上去。達到理想狀態(tài)很難,但是下面有一種相對合適的方法

        • 1.保存某個(gè)非0常數如17,到名為result的int類(lèi)型變量中
        • 2.對對象中每個(gè)“主要域”f,(每個(gè)域由equals方法負責),做下面的工作
          • a.為域計算int型的hash碼c
            • i.如果域是boolean型,計算(f?0:1)。

              ii.如果域是byte型、char型、short型或int型,計算(int)f。

              iii.如果域是long型,計算(int)(f^(f>>>32))。

              iv.如果域是float型,計算Float.floattoIntBits(f)。

              v.如果域是double型,計算Double.doubleToLongBits(f),然后如2.a.iii所示,對long型結果進(jìn)一步處理。

              vi.如果域是對象引用,而且這個(gè)類(lèi)的equals方法又遞歸地調用了equals方法對域進(jìn)行比較,那么對這個(gè)域遞歸地調用hashCode方法。如果需要一種更復雜的比較方式,那么先為這個(gè)域計算出“范式表示”,然后在該“范式表示”上調用hashCode方法。如果域為null,則返回0。

              vii.如果域是數組,則把每個(gè)元素作為分離的域對待。即遞歸地使用這些規則,為每個(gè)“主要元素”計算hash碼。然后用2.b所示方法復合這些值。

          • b.把步驟a中計算出的hash碼c按如下方式與result復合: result = 37*result + c;
        • 3.返回result。
        • 4.完成hashCode方法后,測試是否相同的實(shí)例會(huì )有相同的hash碼,如果不是,找到原因,修正問(wèn)題。
    • - 永遠要重載toString
      • 為類(lèi)提供一個(gè)好的toString實(shí)現可以使類(lèi)使用起來(lái)更加賞心悅目。
      • 實(shí)際使用中,toString方法應該返回包含在對象中的所有令人感興趣的信息。
      • 無(wú)論是否指明格式,都要把你的意圖清楚地文檔化出來(lái)。
    • - 謹慎的重載clone
      • 為了實(shí)現Cloneable接口,會(huì )產(chǎn)生一種古怪的機制:不通過(guò)調用構造函數卻創(chuàng )建了一個(gè)對象。
      • 實(shí)現對像拷貝的精巧的方法是提供一個(gè)拷貝構造函數(copy constructor)??截悩嬙旌瘮导八撵o態(tài)工廠(chǎng)變形與Cloneable/clone方法相比有很多好處
        • 它們不依賴(lài)于那種有風(fēng)險的蹩腳的對象創(chuàng )建機制;
        • 不需要遵守由糟糕的文檔規范的規約;
        • 不會(huì )與final域的正常使用產(chǎn)生沖突;
        • 不要求客戶(hù)不必要地捕獲被檢查的異常;
        • 給客戶(hù)提供了一種類(lèi)型化的對象。
  • 方法
    • - 檢查參數的有效性
      • 如果方法沒(méi)有對參數做檢查,會(huì )出現幾種情形。

        • 方法可能在執行中間失敗退出并給出含糊的異常。
        • 更差的是,方法能正常返回,并計算了錯誤結果。

        對那些不被方法使用但會(huì )被保存以供使用的參數,檢查有效性尤為重要。

        一種重要的例外是有效性檢查開(kāi)銷(xiāo)高,或者不切實(shí)際,而且這種有效性檢查在計算的過(guò)程中會(huì )被隱式地處理的情形。

        總的來(lái)說(shuō),每次在設計方法或設計構造函數時(shí),要考慮它們的參數有什么限制。要在文檔中注釋出這些限制,并在方法體的開(kāi)頭通過(guò)顯示的檢查,對它們進(jìn)行強化。養成這樣的習慣是重要的,有效性檢查所需要的不多的工作會(huì )從它的好處中得到補償。

    • - 使用保護性拷貝
      • 必須在客戶(hù)會(huì )使用一切手段破壞類(lèi)的約束的前提下,保護性地設計程序。

        下面的類(lèi)的目的是表示非可變的時(shí)間周期:
        //Broken "immutable" time period class
        public final class Period {
                 private final Date start;
                 private final Date end;

                 public Period(Date start, Date end) {
                           if(start.compareTo(end) >0 )
                                     throw new IllegalArgumentException(start+" after "+end);
                           this.start = start;
                           this.end = end;
                 }

                 public Date start() {
                           return start;
                 }
                 public Date end() {
                           return end;
                 }

                 ...
        }
        乍看上去,這個(gè)類(lèi)是非可變的,并通過(guò)執行周期的起始點(diǎn)不會(huì )落在周期終止點(diǎn)之后的判斷,增強了類(lèi)的約束。然而,如果Date是可變的,這種約束很容易被違背:
        // Attack the internals of a Period instance
        Date start = new Date();
        Date end = new Date();
        Period p = new Period(start,end);
        end.setYear(78);  //Modifies internals of p!

        為了保護Period實(shí)例的內部細節免于這種攻擊,對構造函數的每一個(gè)可變參數使用保護性拷貝是必要的。

        使用副本代替初始值作為Period實(shí)例的組件:
        //Repaired constructor - make defensice copies of parameters
        public Period(Date start, Date end){
                 this.start = new Date(start.getTime());
                 this.end = new Date(end.getTime());
                 if(this.start.compareTo(this.end)>0)
                           throw new IllegalArgumentException(start+" after "+end):
        }

        保護性拷貝要在參數的有效性檢查的前面,并且有效性檢測要在副本而不是初值上執行。

        盡管替代構造函數成功防止了前面的攻擊,但改變一個(gè)Period實(shí)例仍然是可能的,因為訪(fǎng)問(wèn)器對它的可變的內部細節提供了訪(fǎng)問(wèn)能力。
        //Second attack on the internals of a Period instance
        Date start = new Date();
        Date end = new Date();
        Period p = new Period(start,end);
        p.end().setYear(78);          //Modifies internals of p!

        為了防止第二種攻擊,簡(jiǎn)單地修改訪(fǎng)問(wèn)器,返回可變內部域的保護性拷貝即可:
        //Repair accessors - make defensive copies of internal fields
        public Date start() {
                 return (Date) start.clone();
        }
        public Date end() {
                 return (Date) end.clone();
        }

        只要可能,就應該使用非可變對象作為對象的組件,以便不再關(guān)心保護性拷貝問(wèn)題。

    • - 認真設計方法簽名
      • 認真地給方法選名字。名字要永遠遵守標準的命名慣例。
      • 不要過(guò)于追求提供便利的方法。對接口這一點(diǎn)是千真萬(wàn)確的,接口中方法太多會(huì )使實(shí)現程序和用戶(hù)使用時(shí)變得復雜。
      • 避免長(cháng)參數列表。三個(gè)參數的使用就該作為最大值。類(lèi)型相同的長(cháng)參數序列尤其有害。
        • 有兩種技術(shù)可以大幅縮短常參數列表。一種技術(shù)是將一個(gè)方法分成多個(gè)方法實(shí)現,其中每個(gè)方法僅需要參數類(lèi)表的一個(gè)子集。
        • 第二種技術(shù)是創(chuàng )建助手類(lèi),用來(lái)保持參數集合。
      • 在參數類(lèi)型的使用上,接口優(yōu)于類(lèi)。
      • 謹慎地使用函數對象。
    • - 謹慎地使用過(guò)載
      • 下面是一個(gè)意圖良好的嘗試,按照是否是集、列表還是其他種類(lèi)的集合把集合分類(lèi):
        //Broken - incorrect user of overloading!
        public class CollectionClassifier {
                 public static String classify(Set s) {
                           return "Set";
                 }
                 public static String classify(List l) {
                           return "List";
                 }
                 public static String classify(Collection c) {
                           return "Unkown Collection";
                 }
                 public static void main(String[] args) {
                           Collection[] tests = new Collection[] {
                                     new HashSet(),
                                     new ArrayList(),
                                     new HaspMap().values()
                           };

                           for(int i=0;i<tests.length;i++)
                                     System.out.println(classify(tests[i]));
                 }
        }
        也許認為這個(gè)程序會(huì )依次打出Set、List和Unkown Collection,但實(shí)際是打印3次Unkown Collection。這是因為classify方法被過(guò)載了,選擇使用哪個(gè)調用的決定是在編譯期做出的。

        改正的方法是用一個(gè)顯式instanceof測試實(shí)例的方法代替classify中的三個(gè)過(guò)載方法:
        public static String classify(Collection c) {
                 return (c instanceof Set? "Set" :(c instanceof List ? "List" : "Unkown Collection"));
        }

        一種安全、保守的策略是永遠不要導出兩個(gè)具有相同數目參數的過(guò)載方法。

    • - 返回0長(cháng)度的數組而不是null
      • 下面的方法比較常見(jiàn):
        private List CheesesInStock = ...;

        public Cheese[] getCheeses() {
                 if(cheesesInStock.size() == 0)
                           return null;
                 ...
        }
        沒(méi)有理由需要對無(wú)奶酪可買(mǎi)這種情況做特殊的處理。著(zhù)需要客戶(hù)方提供額外代碼處理null的返回值,例如:
        Cheese[] cheeses = shop.getCheeses();
        if (cheeses != null && Array.asList(shop.getCheeses()).contains(Cheese.STILTON))
                 System.out.println("Jolly good, just the thing.");
        而不是:
        if (Array.asList(shop.getCheeses()).contains(Cheese.STILTON))
                 System.out.println("Jolly good, just the thing.");

        幾乎每次返回null而不是返回0長(cháng)度數組的方法時(shí),都需要這種多余地處理。返回null是易出錯的,因為寫(xiě)代碼的程序員可能會(huì )忘記設計處理返回null的特殊情形的代碼。

        正確的做法是:
        private List cheeseInStock = ...;
        private final static Cheese[] NULL_CHEESE_ARRAY = new Cheese[0];

        public Cheese[] getCheeses() {
               return (Cheese[]) cheesesInStock.toArray(NULL_CHEESE_ARRAY);
        }

  • - 通用編程
    • - 最小化局部變量作用域
      • 通過(guò)最小化局部變量的作用域,可以增加代碼的可讀性和可維護性,減少出錯可能。
      • 最小化局部變量的最有效的方式是在它第一次被使用時(shí)聲明。
      • 幾乎每一個(gè)局部變量聲明都應該包含一個(gè)初始化器(initializer)。
      • 最小化局部變量的最后一個(gè)技術(shù)是使方法小而集中。
    • 需要確切答案時(shí),不要使用float或double類(lèi)型
      • float和double類(lèi)型特別不適合貨幣計算。
      • 假設手中有$1.03,花掉0.42后還剩多少錢(qián)呢?
        System.out.println(1.03 - .42);
        不幸的是,它會(huì )打印0.6100000000000001。
      • 解決這個(gè)問(wèn)題的正確方法是使用BigDecimal、int和long類(lèi)型進(jìn)行貨幣計算。
      • 使用BigDecimal有兩個(gè)缺點(diǎn)。
        • 它不如使用算術(shù)類(lèi)型方便。
        • 速度慢。
    • - 盡量避免使用串
      • 串是值類(lèi)型的糟糕的替代物。
      • 串是可枚舉類(lèi)型的糟糕的替代物。
      • 串是集合類(lèi)型的糟糕的替代物
      • 串是capabilities(能力表)的糟糕的替代物
    • - 了解串并置的性能
      • 為連接n個(gè)串重復地使用串合并操作符需要n的二次方時(shí)間。
      • 為了獲得可接受的性能,可以使用StringBuffer代替String。
    • 通過(guò)接口訪(fǎng)問(wèn)對象
      • 如果存在合適的接口類(lèi)型,那么參數、返回值、變量和域應該用接口類(lèi)型聲明。

        應該養成下面這樣的程序習慣:
        //Good - uses interface as type
        List subscribers = new Vector();

        而不要這樣做:
        //Bad - uses class as type!
        Vector subscribers = new Vector();

        如果養成了使用接口作為類(lèi)型的習慣,程序就會(huì )有更好的擴展性。當希望轉換實(shí)現時(shí),需要做的全部工作就是改變構造函數中類(lèi)的名字。

    • 謹慎地做優(yōu)化
      • 人們通常都把計算機的罪歸咎于效率問(wèn)題(甚至是不必要的獲得的效率),而不去懷疑任何其他的原因——甚至包括盲目地做傻事?!猈illiam A.Wulf
      • 不要計較微小效率的得失,在97%的情況下,不成熟的優(yōu)化是一切罪惡的根源。 ——Donald E.Knuth
      • 做優(yōu)化時(shí),要遵循兩條原則:
        原則1  不要做優(yōu)化
        原則2(僅對專(zhuān)家)  還是不要做優(yōu)化——也可以這么說(shuō):在絕對清楚的、未經(jīng)優(yōu)化的方案之前,不要做優(yōu)化。 
        ——M.A.Jackson
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Java知識體系詳解
java 編程思想 筆記
C#構造函數
靜態(tài)方法跟實(shí)例方法有什么區別
多用靜態(tài)工廠(chǎng)代替構造器
【一分鐘知識】靜態(tài)變量和實(shí)例變量、構造器
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久