《Effective Java中文版第二版》读书笔记

Posted baka-sky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Effective Java中文版第二版》读书笔记相关的知识,希望对你有一定的参考价值。

说明

这里是阅读《Effective Java中文版第二版》的读书笔记,这里会记录一些个人感觉稍微有些重要的内容,方便以后查阅,可能会因为个人实力原因导致理解有误,若有发现欢迎指出。一些个人还不理解的会用斜线标注。

第一章是引言,所以跳过。

第二章 创建和销毁对象

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

含义

静态工厂方法是指一个返回类的实例的静态方法,例如:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean,FALSE;
}

优点

相对于一个类的构造器,静态工厂方法的名称没有限制。

众所周知,构造器的方法名是必须和类名一样的,因此对于有多个参数类型相同的构造方法,一种方法是更改参数的顺序,另一种是增加一个flag来判断执行哪个构造方法。但是这样对于使用者是不友好的,他必须熟悉API或者查阅开发文档。倘若使用静态工厂方法,那么可以通过方法名来给予使用者良好的提示与说明。

不用再每次调用的时候创建一个新的对象。

这句话的典型应用是在设计模式的单例模式中,静态工厂方法能够为重复的调用返回相同的对象。

静态工厂方法可以返回原返回类型的任何子类型的对象。

构造方法是不能使用return语句的,它在使用时也只能产生自身这个类的一个对象,而静态工厂方法可以使用return语句,因此在选择返回对象时就有了更大的灵活性。这个优势的应用很多,比如服务提供者框架模式

小结

应当熟悉静态工厂方法和构造器的各自的长处,在合适的场景使用合适的方法。

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

在面对一个拥有多个属性的类且构造方法拥有多个可选参数时,一个常见的方法是使用重叠构造器模式(创建多个构造方法,每个构造方法比前一个构造方法有新的参数)。例如,第一个构造方法有两个必须参数,第二个构造方法有两个必须参数和一个可选参数,第三个构造方法有两个必须参数和两个可选参数,以此类推。但是当有许多参数的时候,代码会变得很难编写,也很难阅读,甚至会容易出错。

另一个方法是使用javabean模式。因为构造过程被分到了多个调用中(为每个属性的赋值调用该属性的set方法),在构造过程中,javabean可能处于不一致的状态,这种问题难以发现。

第三种方法就是构建器模式(Builder模式)的一种形式。

public class NutritionFacts {

    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final 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;
        }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}


// 使用方法
NutritionFacts n = new NutritionFacts.Builder(200,10).setCalories(20).setFat(30).build();

Builder模式十分灵活,可以利用一个builder来创建多个相同的对象,并且对必须参数和可变参数的实现符合人类的正常思维。另外,对于使用者而言,使用时的代码更容易阅读和编写。

这种方法我在google的protobuf的java实现中见到过。

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

私有构造方法就不提了,这里记录一下第二个:

public enum A {
    INSTANCE;

    public void leaveTheBuilding() {...}
}

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

对于一些只包含静态方法或者静态属性的类(比如工具类),我们不希望他们被实例化。众所周知,在缺少显式构造方法的时候,编译器会默认添加一个无参的构造方法。如果为了严谨,我们可以添加一个私有的构造方法,更可以在这个构造方法中throw异常来中止程序。

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

一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。

除了重用不可变的对象之外,也可以重用那些已知不会被修改的可变对象。

能使用基本数据类型,就尽量不要用对应的封装类。

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

不能以为有了垃圾回收机制后,就不需要考虑内存管理的事情了。

例如用数组来实现栈,当实现出栈操作,size-1后,栈顶坐标后的元素对使用者来说就已经是无效部分了,但是数组仍然拥有对它们的引用,因此垃圾回收机制不会将它们回收。解决办法是在出栈时,将引用置空。

第7条:避免使用终结方法

除了特定情况,不要使用终结方法(finalize)。

子类覆盖了父类的终结方法后,子类的终结方法不会自动调用父类的终结方法,需要手动调用。

以上是关于《Effective Java中文版第二版》读书笔记的主要内容,如果未能解决你的问题,请参考以下文章

Effective Java 英文 第二版 读书笔记 Item 11:Override clone judiciously

《Effective Java(中文第二版)》PDF下载

Effective Java 第二版 Enum

Effective Java通俗理解(持续更新)

[读书笔记]Effective Java 第二章

Effective Java 第三版——48. 谨慎使用流并行