设计模式之单例模式

        单例模式算是设计模式中比较简单,还有大家接触的早的设计模式之一了,单例模式分为恶汉式单例模式和懒汉式单例模式。

一、恶汉式单例

        

/**
 * 恶汉式单例
 */
public class Singleton {

    private static final Singleton singleton = new Singleton();

    private Singleton() {}

    public static Singleton createSignleton(){
        return singleton;
    }
}

测试:

//恶汉式单列测试
Singleton singleton = Singleton.createSignleton();
Singleton singleton1 = Singleton.createSignleton();
System.out.println("singleton == singleton1:" + (singleton == singleton1));

输出:


在一开始就创建实例,这样的单例模式在类一加载的时候就开始创建了,如果长时间不用,就会有种浪费资源的感觉,所以最好是在需要的时候再创建,由此出现了懒汉式单例。

二、懒汉式单例

/**
 * 懒汉式单列
 */
public class SingletonA {

    private static SingletonA singletonA = null;

    private static final Object syncFlag = new Object();

    private SingletonA(){

    }

    public static SingletonA createSingletonA(){
        if (singletonA == null) {
            singletonA = new SingletonA();
        }
        return singletonA;
    }
}

在需要的时候进行创建、

测试:

//懒汉式单列测试
SingletonA singletonA = SingletonA.createSingletonA();
SingletonA singletonA1 = SingletonA.createSingletonA();
System.out.println("singletonA == singletonA1:" + (singletonA == singletonA1));

输出:


这种创建在多线程情况下会出现创建的两个单例对象不相等,所以在创建的时候加上线程锁,保证线程安全,由此出现了线程安全的懒汉式单例:

三、线程安全的懒汉式单例

 
public class SingletonA {

    private static SingletonA singletonA = null;

    private SingletonA(){

    }

    public static SingletonA createSingletonA(){
        if (singletonA == null) {
            synchronized (SingletonA.class){
                //使用双重锁定 保证线程安全
                if (singletonA == null)  {
                    singletonA = new SingletonA();
                }
            }
        }
        return singletonA;
    }
}

如果只有外层一个空判断还是有可能会出现线程不安全,所以双重判断定更能保护线程安全。由于添加了synchronized(会造成线程阻塞)性能上还是多少会有点损耗,(synchronized也可以加在方法上,这样性能上更差)那能不能把synchronized去掉呢?所以又出现了一种单例,内部类形式的懒汉式单例。

四、内部类形式的懒汉式单例

 
public class SingletonB {

    private SingletonB() {
    }

    static class InternalSingletonB{
        private static  final  SingletonB singletonB = new SingletonB();
    }
    public static SingletonB createSingletonB(){
        return InternalSingletonB.singletonB;
    }
}

因为单例对象实例化是在内部类加载的时候构建的,所以是线程安全的(在方法中创建对象,会存在并发问题,而静态内部类是随着方法调用而被加载,只加载一次,不会存在并发问题,所以是线程安全的),而且在createSingletonB()方法上没有添加synchronized关键字,所以不会造成性能上的损耗,当我们调用SingletonB.createSingletonB()创建对象的时候,才去加载内部类InternalSingletonB。由此来达到懒加载的目的。

测试:

//懒汉式单列测试B内部类情况
SingletonB singletonB = SingletonB.createSingletonB();
SingletonB singletonB1 = SingletonB.createSingletonB();
System.out.println("singletonB == singletonB1:"+ (singletonB == singletonB1));

输出:


这个版本的单例在利用反射的时候会出现两个对象不相的情况:

测试:

//利用反射尝试 懒汉式单列测试B内部类情况
SingletonB singletonB2 = SingletonB.createSingletonB();
SingletonB singletonB3 = null;
Class<SingletonB> aClass = SingletonB.class;
try {
    Constructor<SingletonB> constructor = aClass.getDeclaredConstructor();
    constructor.setAccessible(true);
    singletonB3 = constructor.newInstance();
} catch (Exception e) {
    e.printStackTrace();
}
System.out.println("singletonB2哈希值:" + singletonB2.hashCode());
System.out.println("singletonB3哈希值:" + singletonB3.hashCode());
System.out.println("singletonB2=singletonB3:" + (singletonB2 == singletonB3));

输出:


由此又出现了一个单例:

五、内部类形式的懒汉式单例(处理反射之后不相等的情况)

 
public class SingLetonC {


    private static boolean isInit = false;

    private SingLetonC() {
        //防止单例被破坏
        if (isInit) {
            throw new RuntimeException("单例已经被破坏");
        }
        isInit = true;
    }

    static class InternalSingletonC {
        private static final SingLetonC singletonC = new SingLetonC();
    }

    public static SingLetonC createSingletonC() {
        return InternalSingletonC.singletonC;
    }
}

 

测试:

//利用反射尝试 懒汉式单列测试B内部类情况
SingLetonC singletonC2 = SingLetonC.createSingletonC();
SingLetonC singLetonC3 = null;
Class<SingLetonC> cClass = SingLetonC.class;
try {
    Constructor<SingLetonC> constructor = cClass.getDeclaredConstructor();
    constructor.setAccessible(true);
    //创建对象时候单列已经被破坏
    singLetonC3 = constructor.newInstance();
} catch (Exception e) {
    e.printStackTrace();
}
System.out.println("singletonC2哈希值:" + singletonC2.hashCode());
System.out.println("singletonC3哈希值:" + singLetonC3.hashCode());

输出:


这样达到了防止单例被破坏的情况(不用反射创建就行),但是这种单例模式在系列化对象的时候又会出现不相等的问题,先实现系列化接口:

public class SingLetonC implements Serializable {


    private static final long serialVersionUID = -4827468642694587697L;

    private static boolean isInit = false;

    private SingLetonC() {
        //防止单例被破坏
        if (isInit) {
            throw new RuntimeException("单例已经被破坏");
        }
        isInit = true;
    }

    static class InternalSingletonC {
        private static final SingLetonC singletonC = new SingLetonC();
    }

    public static SingLetonC createSingletonC() {
        return InternalSingletonC.singletonC;
    }
}

测试:

SingLetonC singLetonC4 = SingLetonC.createSingletonC();
ObjectOutput objectOutput = null;
try {
    objectOutput = new ObjectOutputStream(new FileOutputStream("file.ser"));
    objectOutput.writeObject(singLetonC4);
    objectOutput.close();
    ObjectInput objectInput = new ObjectInputStream(new FileInputStream("file.ser"));
    SingLetonC singLetonC5 = (SingLetonC) objectInput.readObject();
    objectInput.close();
    System.out.println(" singLetonC4 hashCode = " + singLetonC4.hashCode());
    System.out.println(" singLetonC5 hashCode = " + singLetonC5.hashCode());
    System.out.println(" singLetonC4 = singLetonC5:" + (singLetonC4 == singLetonC5));
} catch (IOException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

输出:


同一个对象系列化之后变成了2个不相等的对象。要保证系列化之后2个对象还相等的话需要添加readResolve()(这个方法是反系列化读取的时候使用)由此进行了改进(改进版单例):

public class SingLetonC implements Serializable{


    private static final long serialVersionUID = -4827468642694587697L;

    private static boolean isInit = false;

    private SingLetonC(){
        //防止单例被破坏
        if(isInit) {
            throw new RuntimeException("单例已经被破坏");
        }
        isInit = true;
    }

    static class InternalSingletonC{
        private static  final  SingLetonC singletonC = new SingLetonC();
    }
    public static SingLetonC createSingletonC(){
        return InternalSingletonC.singletonC;
    }
    /**
     * 提供该方法的实现。readResolve()代替了从流中读取对象
     * 这就确保了在序列化和反序列化的过程中没人可以创建新的实例。
     * 这个方法可以确保类的序列化将会返回怎样的object。
     * readresolve()并不是静态的,在序列化创建实例的时候被引用
     *
     * @return
     */
    private Object readResolve(){
        return createSingletonC();
    }
    
}
 

测试代码还是和上面一样:

输出:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值