[Effective Java]第三话:使用私有构造方法或者枚举来实现单例

三、Item 3:使用私有构造方法或者枚举来实现单例

单例即为只初始化一次的类,我们可以一贯性的认为,单例代表系统唯一的系统组件,就像是window manager(窗口管理器)或者file system(文件系统)。
我们有两种方法实现单例。但是两种方法我们都需要在这个待实现的单例中提供一个私有的构造方法和一个共有静态的方法来处理这个唯一的实例。在其中的一个实现中,我么希望类的成员变量是final修饰的。
  // Singleton with public final field
    public class Elvis {
       public static final Elvis INSTANCE = new Elvis();
       private Elvis() { ... }
       public void leaveTheBuilding() { ... }
   }
在上面的sinleton中,构造方法只会在共有静态成员初始化的时候执行唯一的一次,我们之所以不使用“public”或者“protected”来修饰该类的构造方法是因为只有“private”修饰符才能保证(guarantees)一个独占的(monoelvistic)领域(universe):这样,事实上,只有一个Elvis的实例在内存中(当Elvis这个类被实例化的时候)–不可能多或者少,只此唯一。
第二种创建singleton的方法就是,创建一个共有静态方法:
    // Singleton with static factory
    public class Elvis {
    private static final Elvis INSTANCE = new Elvis(); private  Elvis() { ... }
    public static Elvis getInstance() { return INSTANCE; }
       public void leaveTheBuilding() { ... }
   }
每次调用Elvis.getInstance(),只会返回一个唯一的实例。
共有成员方法最为主要的优点就是:方法本身的声明(getInstance)就已经申明了这个类是单例。所以,该方法永远返回一个实例。
为了能够以实用上述任何一种方法构造的单例序列化,仅仅使得该类实现serializable接口是不充分的,为了保证在序列化的同时,该类仍旧是单例的,你必须为所有的实例域声明为“transient”,并提供一个“readResolve”(该方法来自于serializable接口)方法。否则,每次方序列化一个实例的时候,一个新的实例将会创建。下面就是我们需要在之前的例子中添加的内容。

public class Elvis implements Serializable{

    private Elvis(){}

    /**
     * 实用volatile的作用(只能在JDK 1.5+中进行使用):
     *          1、该变量在线程中不会存在副本,直接从内存中取出
     *          2、该关键字会禁止指令冲排序优化
     */

    /**
     * 之所以要使用volatile关键字的原因
     *        在instance=new Elvis();这个进行实例化的时候,JVM会做出下面的几件事情
     *              1、在堆内存中查找该类是否曾经实例化过,如果没有,需要通过类加载器进行类的加载;
     *              2、为实例分配内存空间;
     *              3、调用类的构造方法进行实例化;
     *              4、将实例化的对象指向分配的内存空间。
     *
     *        只要的两件事情即为:3和4会在JVM执行指令重拍优化的时候将指令执行的顺序进行交换,这样,如果
     *                          过线程在运行的时候后,多于一个线程中需要持有Elvis这个实例的对象,但是,
     *                          JVM执行的顺序是4->3,那么,虽然该实例已经存在分配的内存空间,但是该类
     *                          还未进行实例化,会导致NullPointException.
     */
    private volatile static Elvis instance = null;

    public static Elvis getInstance(){
        if(instance==null){
            synchronized (Elvis.class){
                if(instance==null){
                    instance=new Elvis();
                }
            }
        }
        return instance;
    }

    protected Object readResolve()
    {
        return instance;
    }
}
在JDK 1.5之后,有第三中方法来实现单例,我们同过enum(枚举)来进行实现
public enum Elvis2 {
    INSTANCE;
}
该种方法能够避免绝大多数的不良情况的发生,并且,这其实是实现单例最好的方法
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值