《Effective Java》读书笔记03-私有构造器

一、私有构造器与Singleton

Singleton设计模式就是确保类只被实例化一次,那如何保证类只被实例化一次呢?最好的办法就是阻止客户端自己创建类实例,也就是把创建类实例的构造器给私有化。这样,客户端需要类实例时,我们就可以给它返回已经创建好的实例。这就是私有构造器与Singleton的不解之缘。

创建Singleton的方式

1、将实例做成公有静态成员且是final型的。

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

私有构造器仅被调用一次,用来实例化公有的静态final域Elvis.INSTANCE。

2、将实例做成私有静态成员,然后提供公有的成员静态工厂方法。

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

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.getInstance();
        elvis.leaveTheBuilding();
    }
}
3、编写一个只包含单个元素的枚举类型。

public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}
三种方式比较:

1、公有域方法的主要好处在于,组成类成员的声明很清楚地表明这个类是一个Singleton:公有的静态final域保证引用的都是相同的对象。公有域方法在性能上由于现代JVM实现几乎都能把静态工厂方法的调用内联化而不会有优势。内联是JVM用编译好的二进制直接替换该段函数调用,免去编译过程,比较高效,个人理解,呵呵。。。

2、工厂方法的优势之一是灵活,在不改变约定即API的情况下,我们可以改变该实现是否保持Singleton,实现方式多样化。第二个优势与泛型有关。

3、如果涉及到singleton类序列化的话,这两种方式就没有枚举合适了。当singleton需要序列化时,不仅需要实现序列化接口,还要声明所有实例域都是瞬时的(transient),并提供一个readResolve方法。否则,每次反序列化时,都会生成一个新的实例。

加入readResolve方法保证singleton属性:

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    private Object readResolve() {
        // Return the one true Elvis and let the garbage collector
        // take care of the Elvis impersonator.
        return INSTANCE;
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

最佳编程实践:

1、如果singleton不需要序列化的话,可以选择3种方式的任一种,根据自己的喜好,但前两种可以通过反射调用私有构造器来生成第二个实例。

2、如果需要序列化,则推荐使用单元素的枚举类型来实现Singleton。因为,它简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候,你看,多好。。。


二、私有构造器与不可实例化

一般我们编写工具类时都希望这些类不可实例化,因为实例对它没任何意义。如果我们不提供显示构造器的话,编译器会为我们创建一个默认的无参构造器,这不能阻止客户实例化工具类。企图通过将类做成抽象类来强制该类不可实例化也是行不通的,因为,客户可以实例化它的子类。这样做还容易给你一个误解,以为该类就是用来被继承的,显然违背了我们的本意。

我们可以通过将构造器私有化,来达到类不可实例的目的,虽然这样做会造成该类不能被子类化,一般工具类都不需要被子类化吧,呵呵,需要的话可以做成单粒的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值