Effective Java读书笔记--第2章 创建和摧毁对象

个人读书笔记,部分没读懂的知识点可能会简单概括或缺失,以后反复阅读后再完善。

第二章 创建和摧毁对象

第1条:使用静态工厂方法替代构造器

静态工厂方法的好处。

1、有命名,更方便阅读。
2、不用每次调用都创建新对象(构造器需要)。
3、可以返回原返回类型的任何子类型对象(更灵活)。
4、创建参数化类型实例使代码更简洁。
例子:

public static <K, V> HashMap<K, V> newInstance() {
        return new HashMap<>();
}
    
Map<String, List<String>> m =Singleton.newInstance();

静态工厂方法的坏处。

1、类如果不含有公有或者受保护的构造器,就不能被子类化。
2、与静态方法没有区别(不方便阅读?)

第2条:遇到多个构造器时要考虑用构建器

类中有多个参数,一般使用的重叠构造器(telescoping constructor)模式有不好的地方,比如有些不想设置的参数也不得不传值。

解决办法提到了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模式调用代码:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                    .setCalories(100)
                    .setSodium(35)
                    .setCarbohydrate(27)
                    .build();

如果类的构造器或者静态工厂中具有多个参数,推荐Builder模式。

第3条:用私有构造器或者枚举类型强化Singleton属性

Singleton指仅仅被实例化一次的类。

java 1.5以前,实现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() {}
}

第二个方法(工厂方法)更灵活。使用泛型更优(第27条)
java 1.5之后实现Singleton的第三种方法:

public enum Elvis3 {
    INSTANCE;
    public void leaveTheBuilding() {}
}

第4条:通过私有构造器强化不可实例化的能力

有些工具类不希望被实例化,企图通过将类做成抽象类来强制该类不可被实例化是行不通的。

可以让这个类包含有私有构造器:

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

该做法的副作用:使得一个类不能被子类化。

第5条:避免创建不必要的对象

最好能重用对象,而不是每次需要的时候创建一个相同功能的新对象。
极端例子:

String s = new String("stringette");

如果上面这种写法用在循环中,会创建成千上万不必要的String实例。
正常写法:

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;
    }
}

重用后例子(调用速度明显增快):

class Person{
    private final Date birthDate;

    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
    /*
     *改进后的做法(快了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;
    }
}    

要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。

例子:

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,意味着程序构造了许多的多余的Long实例。运行时间大大降低。

第6条:消除过期对象的应用

一个存在内存泄露的程序:

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();
        }
        //这样写会有内存泄漏
        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);
        }
    }
}

代码中通过清空过期应用解决内存泄漏:

Object result = elements[--size];
    elements[size]=null;
    return result;

第7条:避免使用过期方法

讲终结方法(finalizer)通常不可预测,一般情况下是不必要的。
使用终结方法还会有性能损失。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,188评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,464评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,562评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,893评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,917评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,708评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,430评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,342评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,801评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,976评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,115评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,804评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,458评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,008评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,135评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,365评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,055评论 2 355

推荐阅读更多精彩内容

  • Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参...
    圣骑士wind阅读 385评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,641评论 18 399
  • 目录 第二章 创建和销毁对象 1 考虑用静态工厂方法替代构造器 对于代码来说, 清晰和简洁是最重要的. 代码应该被...
    高广超阅读 1,452评论 0 12
  • 釆桑子 农村娃 (雅俗共赏) 立志读书求出路,三十左右,都市街头,手持文凭心凉透...
    823b3d92bead阅读 185评论 0 0
  • 什么是格局?是有两个方面,一个是针对自己,一个是针对他人。对自己就是负责任担当的能力。针对他人,就是识人,用人爱人...
    涓涓1016阅读 216评论 0 0