解析设计模式--单例(Singleton)

单例设计模式:

       保证一个类仅有一个对象,并提供一个访问它的全局访问点。

 

比较常见的实现方式是通过静态内部类实现。

public class Singleton{
	
	private Singleton() {}
	
	private static class SingletonInstance{
		private static final Singleton  instance = new Singleton(); 
	}
	
	public static Singleton getInstance() {
		return SingletonInstance.instance;
	}
	
	public void doSomething() {}
	
}
  • 内部静态类不会自动初始化,只有调用静态内部类的方法,静态域,或者构造方法的时候才会加载静态内部类。保证了该类在未调用getInstance()之前不会因为初始化对象而浪费内存。
  • static关键字修饰的变量被称为静态变量。静态变量随着类的加载而存在。静态变量不属于对象,属于类,所以不需要为内部类创建对象就可直接使用该变量。

关于静态变量,我记得18年在《网易公开课》上看了《麻省理工学院公开课:计算机科学及编程导论》的公开课,教授曾经说过这么一句话“Static semantics basically says which programs are meaningful.”

  • final关键字保证了instance初始化之后就不可被改变。

但是该方式有个缺点:不提供序列化机制。

 

《effective java 3th》一书中还推荐了另一种实现方法:使用Enum类实现单例

public enum Singleton {

	INSTANCE;
	
	public void doSomeThing(){}
}

首先来看一下最简单的Enum类

public enum Color{
	RED, GREEN, BLUE;
}

通过反编译之后得到以下源码(此处删掉了values()和valueOf(String s)方法)

public final class Color extends Enum
{

    private Color(String s, int i)
    {
        super(s, i);
    }

    public static final Color RED;
    public static final Color GREEN;
    public static final Color BLUE;
    private static final Color ENUM$VALUES[];

    static 
    {
        RED = new Color("RED", 0);
        GREEN = new Color("GREEN", 1);
        BLUE = new Color("BLUE", 2);
        ENUM$VALUES = (new Color[] {
            RED, GREEN, BLUE
        });
    }
}

由此可以看出,枚举类型Color是Enum类的一个子类,因为java只有单继承,所以该类被定义为final class。

枚举类型中定义的枚举变量是静态常量类型static final ,初始化在static{}域中进行。即每一个枚举类型的枚举变量在JVM中都是唯一的。只有在该枚举类型第一次被加载的时候,枚举变量才会被初始化,且初始化之后不可被更改。

还有一点尤为重要:使用Enum实现单例模式还无偿地提供了序列化机制,保证单例。

java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。

反序列化是通过调用readObject()方法实现的,但是该方法返回的是一个新创建出来的对象

而Enum在序列化时仅仅将枚举变量的name属性输出到结果中,反序列化的时候是通过Enum中的valueOf()方法来根据name查找枚举对象。同时Enum禁用了readObject()方法,使其在被调用的时候抛出异常。这就保证了反序列化时不会返回一个新的对象。

Enum源码如下:

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }
  
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}

 

总结:

在不考虑序列化的情况下,推荐使用静态内部类的方式实现单例模式。否则推荐使用Enum实现单例模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值