《effective java》读书笔记1(创建和销毁对象)

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

1.what is?
此处的静态工厂方法与设计模式中的工厂模式不一样。

比如类

class Person{

    //A的构造器
        public A(){};

    //A的静态工厂方法可以是
        static Person  Male;       //男人集合
        static Person  Female;   //女人集合
    public static getMale(){
        return Person.Male;
    }
}

2.why?
》第一个优势:它们有直接的名称。而重载的构造器没有名称,没有说明的情况下可能很难辨认。
》第二个优势:不必每次都创建新对象。通过构造器每次都new出一个对象,而像上例的getMale则可以重用一个对象。
》第三个优势:可以返回任何子类的对象,同时又不会让对象的类变成公有的,更加灵活,api更加简洁。同时在编写该静态工厂方法时,子类可以不必存在。

e.g.:服务提供者框架。其中有三个重要的组件:服务接口,服务提供者注册API(注册后客户端可访问该服务),服务访问API(客户端获取服务实例)。第四个可选组件是服务提供者接口(用于创建其服务实现的实例),如果没有,就按照类名注册,并通过反射方式实例化。

》第四个优势:创建参数化类型实例的时候,它们使代码变得更加简洁。

e.g.:Map<String,List<String>> m = new HashMap<String,List<String>>();
可以使用静态工厂方法进行类型推导
public static <K,V> HashMap<K,V> newInstance(){ return new HashMap<K,V> ();}

貌似jdk1.7里面就是这么干的。

3.but
》第一个缺陷:类如果不含public或者protected的构造器,就不能被子类化
》第二个缺陷:与其他静态方法没有区别,不好辨认。

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

1.what?
当构造器有n个参数时程序员习惯用重叠构造器

class A{
    public A(){this.A(null)}
    public A(B){
        this.A(B,null)
    }
    public A(B,C){
        this.A(B,C,null)
    }
    public A(B,C,D)
    ….
}

很淡疼对不对?

替代品有JavaBeans:

class A{
    B,C,D;
    public A(){}
        //getter and setter
}

但是这样分成多个步骤创建实例,会产生多线程下不安全的问题,必须保证线程安全的情况下再这么做。

于是第三种替代品出现,Builder模式

class A{
    B,C,D;
    static class Builder{
        B,C,D;
        public Builder(){}
        public Builder setB(B){
            this.B = B;
            return this;
        }
        public Builder setC(C){
            this.C = C;
            return this;
        }
        public Builder setD(D){
            this.D = D;
            return this;
        }
        public A build(){
            return new A(this);
        }
    }

    private A(Builder b){
        this.B = b.B;
        this.C = b.C;
        this.D = b.D;
    }
}

相信都见过,这种叫做构建器。在build或者setter方法中对参数做强约束检查。

缺陷:需要先new构建器,参数设置也冗长,如果参数较少(3个以内)可以不用。

总之,如果类的构造器或者静态工厂中具有多个参数时,Builder模式是种不错的选择。

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

单例大家都很熟悉,用的多。比如

class A{
    private final static A INSTANCE = new A();
    private A(){}
    public void getInstance(){
        return INSTANCE;
    }
}

但是通过反射机制依然是可以访问私有属性的。而且如果A需要序列化,加上implements Serrializable是不够的,为了保证单例,必须声明所有实例域都是transient的,并提供readResolve方法,否则每次反序列化都会创建一个新实例。

private Object readResolve(){
    return INSTANCE;
}

从jdk1.5开始,实现单例还有第三种方法,利用包含单个元素的枚举类型:

public enum A{
    INSTANCE;
}

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

第5条:避免创建不必要的对象。避免无意识的自动装箱。小对象的创建销毁很廉价,不要用对象池维护,消耗大的比如数据库会采用连接池。

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

》情况一:Stack类自己管理内存,对于pop出的数据自己认为是过期的,但是对于垃圾回收器来说并不清楚,所以pop的时候主动释放才能避免内存泄漏。类似这种情况,只要类是自己管理内存,就应该警惕内存泄漏问题。

》情况二:缓存中的对象引用
》情况三:监听器和其他回调。
(ps:以上情况我在安卓开发中亲身经历过无数遍)

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

1)终结方法不一定及时执行,且有性能损失。

2)替代方案是显式定义一个的终止方法,并要求该类的每个实例不再有用时调用该方法。如果该类已经被终止,再次调用应该抛出IllegalStateException
(ps:这个也是,安卓开发中有时候activity无法释放,需要自己手动控制释放)

最优设计:
try{}catch(){}finally{ 自定义的终止方法 },java的io流都有自己的终止方法。

3)
》终结方法的好处一:当你忘了调用自己的终止方法时,终结方法可以充当安全网,迟到总比不到好。如果到了这一步应该加个错误日志,这是一个bug
》终结方法的第二个合理用途:当java对象通过native方法委托给一个本地对象时,称为本地对等体,很明显java的垃圾回收器是不知道它的,java对象被回收时这个本地对象还在蹦哒。当它不具有关键资源的情况下,终结方法就是最合适的。如果具有关键资源,就需要显示的终止方法。

4)终结方法链不会自动执行,应该在try块中终结子类,然后finally块中调用超类的终结方法。

5)总之,除非作为安全网,或者是终止非关键的资源,否则不要使用终结方法。

猜你喜欢

转载自blog.csdn.net/kkae8643150/article/details/79306037