Effective Java讀書筆記--第2章 創(chuàng)建和摧毀對象

個(gè)人讀書筆記,部分沒讀懂的知識點(diǎn)可能會(huì)簡單概括或缺失,以后反復(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è)構(gòu)造器時(shí)要考慮用構(gòu)建器

類中有多個(gè)參數(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)工廠中具有多個(gè)參數(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() {}
}

第二個(gè)方法(工廠方法)更靈活。使用泛型更優(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è)類包含有私有構(gòu)造器:

public class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}

該做法的副作用:使得一個(gè)類不能被子類化。

第5條:避免創(chuàng)建不必要的對象

最好能重用對象,而不是每次需要的時(shí)候創(chuàng)建一個(gè)相同功能的新對象。
極端例子:

String s = new String("stringette");

如果上面這種寫法用在循環(huán)中,會(huì)創(chuàng)建成千上萬不必要的String實(shí)例。
正常寫法:

String s = "stringette";

有些已知不會(huì)被修改的可變對象,也可以重用。
重用前例子:

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)心無意識的自動(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)行時(shí)間大大降低。

第6條:消除過期對象的應(yīng)用

一個(gè)存在內(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();
        }
        //這樣寫會(huì)有內(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é)方法還會(huì)有性能損失。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Effective Java筆記一 創(chuàng)建和銷毀對象 第1條 考慮用靜態(tài)工廠方法代替構(gòu)造器 第2條 遇到多個(gè)構(gòu)造器參...
    圣騎士wind閱讀 429評論 0 2
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,638評論 18 399
  • 目錄 第二章 創(chuàng)建和銷毀對象 1 考慮用靜態(tài)工廠方法替代構(gòu)造器 對于代碼來說, 清晰和簡潔是最重要的. 代碼應(yīng)該被...
    高廣超閱讀 1,523評論 0 12
  • 釆桑子 農(nóng)村娃 (雅俗共賞) 立志讀書求出路,三十左右,都市街頭,手持文憑心涼透...
    823b3d92bead閱讀 262評論 0 0
  • 什么是格局?是有兩個(gè)方面,一個(gè)是針對自己,一個(gè)是針對他人。對自己就是負(fù)責(zé)任擔(dān)當(dāng)?shù)哪芰?。針對他人,就是識人,用人愛人...
    涓涓1016閱讀 242評論 0 0

友情鏈接更多精彩內(nèi)容