《Effect Java》第二章"创建和销毁对象”笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_29611427/article/details/83176695

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

首先要指明一个误区:静态工厂方法不是指的设计模式里面的工厂方法,他是指以静态方法的形式创建对象(工厂就是用来创建对象的),例如:

public static User createNormalUser(){
    return new User();
}

public static User createVIPUser(){
    return new User(100);
}

书中描述这样做的优点有以下几点:

1.他们有名称。通过以上例子就能看出来,有无名称的好处,创建普通用户和创建VIP用户的区别仅仅在于创建是是否传入参数,显然静态工厂方法更容易使用,产生的客户端代码也更易阅读。

2.不必在每次调用它们的时候创建一个新对象。这条指的是单例模式的用法。

3.它们可以返回类型的任何子类型的对象。这句话不太好理解,最起码我是这样。刚开始我以为是向上转型,可是转念一想,向上转型还需要静态工厂方法?然后仔细一看是返回的子类对象。像Collections里的方法:

public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
}

不管是UnmodifiableRandomAccessList类还是UnmodifiableList类,它们都是在Collections里面的内部类。Collections里面包含大量的这样的内部类。书中也说明了这样的好处,可以返回对象,同时又不会使对象的类变为共有,这样使得实现类变得非常简洁。大家可以自己去查看Collections类去体会一下。

4.在创建参数化类型实例的时候,他们使得代码更简单。书中例子:

//以前版本
Map<String, List<String>> m = new HashMap<String, List<String>>();
//改为静态工厂后
public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}
//实例化
Map<String, List<String>> m = HashMap.newInstance();

其实这个优点对于现在的JDK版本来说也不叫优点了。

//1.7之后
Map<String, List<String>> m =new HashMap<>();

书中阐述的静态工厂方法缺点有两个。

1.类如果不含共有的或者受保护的构造器,就不能被实例化。例如Collections里面的UnmodifiableRandomAccessList和UnmodifiableList就不能被实例化。

2.与其他静态方法没有任何区别。所以无法区别哪些是静态工厂方法和其他静态方法。书中鼓励用命令规范来弥补这个缺点。以下是常用名称:

 valueOf(),of(),getInstance(),newInstance(),getType(),newType()

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

 大家肯定都遇到过这种情况:

public Human(int height, int age) {
        this.height= height;
        this.age= age;
    }

public Human(int age) {
        this.age= age;
}

public Human() {}

public static void main(String[] args) {
   Human h= new Human(170,17);
}

当创建一个对象时,对于某些可选的参数属性,大多数人都是用重载构造器来实现,也就是书中所述重叠构造器。这样做的缺点在于创建对象的时候,表达不清晰,容易出错。例如new Human(170,17),如果你把170和17填返了,那么程序还是会正常运行的。对于参数更多的对象,可能一点不小心就会造成一些微妙的错误,而且程序并不能给我们这些错误任何提示。

然后书中阐述了另外一种方式,javabean模式。即:

private int height;
private int age;

public void setHeight(int height) {
    this.height = height;
}

public void setAge(int age) {
    this.age = age;
}

public static void main(String[] args) {
    Human h= new Human();
    h.setHeight(170);
    h.setAge(17);
}

这样的好处在于设置参数的时候有了名字,这样就大大减轻了出错的几率。阅读起来也很容易。可是这也同样有着严重的缺陷,因为构造被分到了几个调用中,所以可能会出现线程安全的问题。实例化类应该是一气呵成的。

因此书中阐述了第三个解决方案:构建器

public class Human {

    private int height;
    private int age;
    private String body;

    public Human(Builder builder) {
        this.age = builder.age;
        this.body = builder.body;
        this.height = builder.height;
    }

    static class Builder{
        private int height=0;
        private int age;
        private String body="";

        public Builder(int age) {
            this.age = age;
        }

        public Builder setHeight(int height) {
            this.height = height;
            return this;
        }

        public Builder setAge(int age) {
            this.age = age;
            return this;
        }

        public Builder setBody(String body) {
            this.body = body;
            return this;
        }

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

    public static void main(String[] args) {
        Human human = new Builder(17).setHeight(170).build();
    }
}

这样优点在于既能保证重叠构造器的安全性,又能保证javaBean模式的可读性。完美融合了前面两种的优点。而且还能保证某些必要的参数,例如age属性。构建器的缺点在于创建对象的时候,必须先创建Builder构建器,增加了创建对象的额外开销。

猜你喜欢

转载自blog.csdn.net/qq_29611427/article/details/83176695