【JAVA】 Effective JAVA 學習筆記

https://www.cnblogs.com/yulinfeng/archive/2017/08/03/7282649.html

================================================================================

2.創建和銷燬對象

2.1 靜態工廠方法代替構造器

2.2 多個構造器參數時用構造器

如下例子:

一個類的構造方法需要多個形參,但是其中某些形參可選可不選:

方案1:重疊構造器:

public class Example {
    public final int A;
    public final int B;
    public final int C;
    public final int D;
   
    public Example (int a , int b){
        this.Example (a,b,0)
    }

    public Example (int a , int b , int c){
        this.Example (a,b,c,0)
    }

    public Example (int a , int b , int c , int d ){
        A =a;
        B =b;
        C =c;
        D =d;
    }
}

缺點:缺少的形參是估計倒數幾個,不可有中間缺少形參情形;

方案2:JAVA BEANS:

public class Example {
    public final int A;
    public final int B;
    public final int C;
    public final int D;

    public Example {}
   
    public void setA(int val) {
        A = val;
    }

    public void setB(int val) { ... }

    public void setC(int val) { ... }

    public void setD(int val) { ... }
}

缺點: 多線程下,不同線程對同一個實參的實例化可能導致不一致狀態出現

方案3:Builder 模式:

public class Example {
    // 都是private
    private final int A;
    private final int B;
    private final int C;
    private final int D;

    public static class Builder {

        //Example 類必須的構造參數:
        private final int A ;
        private final int B ;

        //Example 類可選的構造參數:
        private final int C = 0 ;
        private final int D = 0 ;

        public Builder (int a , int b) {
            A = a;
            B = b;
        }
        
        public Builder setC(int c) {
            C = c;
            return this;
        }

        public Builder setD(int d) {
            D = d;
            return this;
        }
  
        public Example build() {
            return new Example(this);
        }
    }

    public Example (Builder builder) {
        A = builder.A;
        B = builder.B;
        C = builder.C;
        D = builder.D;
    }
}

初始化的例子:

Example ex = new Example.Builder(100,200).setC(300).setD(400).build();

1.(核心): 在目標類中創建 一個內部靜態類---> 通過確保 目標類的初始化完全依賴於其內部靜態類,確保多線程安全

2.目標類的成員變量爲private 域,內部靜態類的成員變量爲private 域,但是setC 爲public 域---->確保 目標類的初始化完全依賴內部類;

3.內部靜態類 的setC() 方法返回不是void 而是目標類 的類型,確保可以連續使用 setC().setD().setE().setD() ,一次性完成成員變量的初始化;

以下方法貌似也可以... ...

沒有builder 顯的不那麼靈活

public static class Example {

    private int A ;
	private int B ;
	
	private int C = 0;
	private int D = 0;
	
	public Example (int a , int b) {
	    A = a;
		B = b;
	}
	
	public Example setC (int c) {
	    C = c;
		return this;
	}
	
	public Example setD (int d) {
	    D = d;
		return this;
	}
}


Example ex = new Example(100,200).setC(300).setD(400);

2.3 用私有構造器或者枚舉類型強化Singleton屬性

2.4 通過私有構造器強化不可實例化能力

public static class Example {

    ... ...

	private Example (int a , int b) {
        ... ...
	}

}

構造函數爲private 域,在初始化Example 是不能通過其構造函數直接初始化,確保此類不可實例化

2.5 避免創建不必要的對象

案例1:

public class Example {

    private final int Date;

    public Example();

    public boolean isBaby {
        Calender cal = Calender.getInstance(...);
        return cal.isBaby(Date);
    } 
}

每次調用Example.isBaby 都會new 一個Calender對象

優化:

public class Example {

    private final int Date;
    private Builder build;
    
    private static class Builder {
        public Calender cal;
        public Builder () {
            cal = Calender.getInstance(...);
        }
    }   
 
    public Example (int val) {
        Date = val;
        build = new Builder();
    }

    public boolean isBaby() {
        return build.cal.isBaby(Date);
    } 
}

///////////////////或者/////////////////

public class Example {

   private final int Date;

   public static Calender cal;

   public Example (int date) {
       Date = date;
       // cal 是static 修飾的,第一次會初始化,後面的實例中直接共享第一次初始化的內存,不會再運行Calender.getInstance() 方法。
       cal = Calender.getInstance(...);
   }

   public boolean isBaby () {
       return cal.isBaby();
   }

}


public class Example {

   private final int Date;

   public Calender cal;

   public Example (int date) {
       Date = date;
       cal = Calender.getInstance(...);
   }

   public boolean isBaby () {
       return cal.isBaby(Date);
   }

}

////////////////////////////////////////////
public class Example {

   private final int Date;

   public Calender cal;

   public Example (int date) {
       Date = date;
   }

   public static boolean isBaby () {
       cal = Calender.getInstance(...);
       return cal.isBaby(Date);
   }
}


boolean IsBaby = Example(19900101).isBaby();//////???????

2.6 消除過期的對象引用

2.7 避免使用終結方法

 

3. 對所以對象都通用的方法

3.1 覆蓋equals 時請遵守通用約定

equals方法實現了等價關係:

1.自反性: 對於任何非null的引用值x,x.equals(x) 必須返回true;

2.對稱性:對於任何非null的引用值x,y 當且僅當 y.equals(x) 返回true時,x.equals(y)必須返回true;

3.傳遞性:對於任何非null的引用值x,y,z 如果x.equals(y)返回true,並且y.equals(z)返回true,那麼x.equals(z)也爲true;

4.一致性:對於任何非null的引用值x,y 只要equals 的筆記操作在對象中所用的信息沒有被修改,多次調用x.equals(y) 就會一直返回true/false;

5.對於任何非null引用值,x.equals(null)必須返回false;

以下是高質量使用equals 的訣竅:

1.使用“==” 操作符檢查                        “參數是否爲這個對象的引用”

2.使用instanceof 操作符檢查             “參數是否爲正確的類型”

3.把參數轉換成正確的類型;

4.對於該類中的每個“關鍵”域,檢查參數中的域是否與該對象中對應的域相匹配。

 

對於 不是float / double 類型的基本類型域, 用“==”比較

對應 對象引用域,可以遞歸滴調用equals方法;

對於float域,可以使用Float.compare 方法

對於double域,則使用Double.compare

 

3.2 覆蓋equals時總要覆蓋hashCode

3.3 始終要覆蓋toString

3.4 謹慎覆蓋clone

3.5 考慮實現Compareable接口

 

4.類和接口

 

4.1 使類和成員的可訪問性最小化

4.2 在公有類中使用訪問方法而非公有域

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章