個人讀書筆記供搀,部分沒讀懂的知識點(diǎn)可能會簡單概括或缺失刚盈,以后反復(fù)閱讀后再完善男杈。
第二章 創(chuàng)建和摧毀對象
第1條:使用靜態(tài)工廠方法替代構(gòu)造器
靜態(tài)工廠方法的好處。
1罗售、有命名,更方便閱讀钩述。
2寨躁、不用每次調(diào)用都創(chuàng)建新對象(構(gòu)造器需要)。
3牙勘、可以返回原返回類型的任何子類型對象(更靈活)职恳。
4、創(chuàng)建參數(shù)化類型實(shí)例使代碼更簡潔方面。
例子:
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<>();
}
Map<String, List<String>> m =Singleton.newInstance();
靜態(tài)工廠方法的壞處放钦。
1、類如果不含有公有或者受保護(hù)的構(gòu)造器恭金,就不能被子類化操禀。
2、與靜態(tài)方法沒有區(qū)別(不方便閱讀横腿?)
第2條:遇到多個構(gòu)造器時要考慮用構(gòu)建器
類中有多個參數(shù)颓屑,一般使用的重疊構(gòu)造器(telescoping constructor)模式有不好的地方斤寂,比如有些不想設(shè)置的參數(shù)也不得不傳值。
解決辦法提到了javaBeans模式與Builder模式揪惦。
javaBeans模式:
public class NutritionFacts {
private int servingSize = -1;
private int servings = -1;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() {
}
public void setServingSize(int servingSize) {
this.servingSize = servingSize;
}
public void setServings(int servings) {
this.servings = servings;
}
public void setCalories(int calories) {
this.calories = calories;
}
public void setFat(int fat) {
this.fat = fat;
}
public void setSodium(int sodium) {
this.sodium = sodium;
}
public void setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
}
}
Builder模式:
public class NutritionFacts {
private int servingSize;
private int servings;
private int calories;
private int fat;
private int sodium;
private int carbohydrate;
public static class Builder {
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder setCalories(int calories) {
this.calories = calories;
return this;
}
public Builder setFat(int fat) {
this.fat = fat;
return this;
}
public Builder setSodium(int sodium) {
this.sodium = sodium;
return this;
}
public Builder setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
}
}
private NutritionFacts(Builder builder) {
servingSize=builder.servingSize;
servings=builder.servings;
calories=builder.calories;
fat=builder.fat;
sodium=builder.sodium;
carbohydrate=builder.carbohydrate;
}
}
Builder模式調(diào)用代碼:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.setCalories(100)
.setSodium(35)
.setCarbohydrate(27)
.build();
如果類的構(gòu)造器或者靜態(tài)工廠中具有多個參數(shù)遍搞,推薦Builder模式。
第3條:用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性
Singleton指僅僅被實(shí)例化一次的類丹擎。
java 1.5以前尾抑,實(shí)現(xiàn)Singleton的兩種方法:
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {}
private static Elvis getInstance(){
return INSTANCE;
}
public void leaveTheBuilding() {}
}
或:
public class Elvis2 {
private static Elvis2 ourInstance = new Elvis2();
public static Elvis2 getInstance() {
return ourInstance;
}
private Elvis2() {
}
public void leaveTheBuilding() {}
}
第二個方法(工廠方法)更靈活。使用泛型更優(yōu)(第27條)
java 1.5之后實(shí)現(xiàn)Singleton的第三種方法:
public enum Elvis3 {
INSTANCE;
public void leaveTheBuilding() {}
}
第4條:通過私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力
有些工具類不希望被實(shí)例化蒂培,企圖通過將類做成抽象類來強(qiáng)制該類不可被實(shí)例化是行不通的再愈。
可以讓這個類包含有私有構(gòu)造器:
public class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
該做法的副作用:使得一個類不能被子類化。
第5條:避免創(chuàng)建不必要的對象
最好能重用對象护戳,而不是每次需要的時候創(chuàng)建一個相同功能的新對象翎冲。
極端例子:
String s = new String("stringette");
如果上面這種寫法用在循環(huán)中,會創(chuàng)建成千上萬不必要的String實(shí)例媳荒。
正常寫法:
String s = "stringette";
有些已知不會被修改的可變對象抗悍,也可以重用。
重用前例子:
class Person{
private final Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
/*
* 效率低下的方法
* */
public boolean isBabyBoomer() {
Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date boomStart=gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd=gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
重用后例子(調(diào)用速度明顯增快):
class Person{
private final Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
/*
*改進(jìn)后的做法(快了250倍)
* */
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calendar gmtCal=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START=gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END=gmtCal.getTime();
}
public boolean isBabyBoomer2() {
return birthDate.compareTo(BOOM_START) >= 0 &&
birthDate.compareTo(BOOM_END) < 0;
}
}
要優(yōu)先使用基本類型而不是裝箱基本類型钳枕,要當(dāng)心無意識的自動裝箱缴渊。
例子:
public static void main(String[] args) {
Long sum=0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
}
sum的聲明是Long而不是long,意味著程序構(gòu)造了許多的多余的Long實(shí)例。運(yùn)行時間大大降低鱼炒。
第6條:消除過期對象的應(yīng)用
一個存在內(nèi)存泄露的程序:
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
//這樣寫會有內(nèi)存泄漏
return elements[--size];
/*
* 清空過期的引用
* */
//Object result = elements[--size];
//elements[size]=null;
//return result;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
代碼中通過清空過期應(yīng)用解決內(nèi)存泄漏:
Object result = elements[--size];
elements[size]=null;
return result;
第7條:避免使用過期方法
講終結(jié)方法(finalizer)通常不可預(yù)測衔沼,一般情況下是不必要的。
使用終結(jié)方法還會有性能損失昔瞧。