单例模式的几种实现方式

1.饿汉

  • 如果应用程序总是创建并使用单例实例或在创建和运行时开销不大
class Single {
    private Single(){}

    private static Single single= new Single();

    public static Single getInstance(){
        return single;
    }
}

2.懒汉

  • 如果开销比较大,希望用到时才创建就要考虑延迟实例化
  • Singleton的初始化需要某些外部资源(比如网络或存储设备)
class Single {
    private Single(){}

    private static Single single= null;

    public static Single getInstance(){
        if ( single == null ) {
            synchronized (Single.class) {
                if ( single == null ) {
                    single = new Single();
                }
            }

        }
        return single;
    }
}

3.静态内部类

class Single {
    private Single(){}

    private static class SingleHandler{
        private static Single single = new Single();
    }

    public static Single getInstance(){
        return Single.SingleHandler.single;
    }
}

4.枚举

public class Single {

    private Single(){}

    public enum SingleEnum {
        singleHandler;
        private Single single;
        private SingleEnum () {
            single = new Single();
        }

        public Single getSingle() {
            return single;
        }
    }

    public static Single getInstacne() {
        return SingleEnum.singleHandler.getSingle();
    }
}

5.单例出现的问题,序列化与反序列化

  • 如果对实现了Serializable的对象进行序列化后,再反序列化,类中会不只一个实例了,因为反序列化时会重新生成一个对象。
  • 将对象写入流时需要指定要使用的替代对象的可序列化类,应使用准确的签名来实现此特殊方法:Object writeReplace() throws ObjectStreamException;
  • 在从流中读取类的一个实例时需要指定替代的类应使用的准确签名来实现此特殊方法:Object readResolve() throws ObjectStreamException;
  • 上述两个方法的只要出现,就会履盖以下两个方法(这两个方法本质的意义就是用来替换序列与反序列的对象),虽然会执行它们,但最后得到的结果却是writeReplace、readResolve两个方法写入或读出的对象
  • 所以只需要在单例类中加上readResolve方法就能避免此问题
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Run {
    public static void main(String[] args) throws Exception {
        Single instance = Single.getInstance();
        System.out.println(instance.hashCode());
        System.out.println(copyInstance(instance).hashCode());
    }

    private static Single copyInstance(Single instance) throws Exception{
        FileOutputStream fos = new FileOutputStream("d:/a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
        Single single2 = (Single)ois.readObject();
        oos.close();
        ois.close();
        return single2;
    }
}


class Single implements Serializable{
    private static final long serialVersionUID = 1L;

    private Single(){}

    private static Single single= null;

    public static Single getInstance(){
        if ( single == null ) {
            synchronized (Single.class) {
                if ( single == null ) {
                    single = new Single();
                }
            }

        }
        return single;
    }

    private Object readResolve() {
        return single;
    }

}

6.单例出现的问题,反射

  • 通过反射获取构造函数,然后调用setAccessible(true)就可以调用私有的构造函数
  • 如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常
public class Run {
    public static void main(String[] args) throws Exception {
        //使用反射方式直接调用私有构造器
        Class<Single> clazz = (Class<Single>)Class.forName("com.lebron.test.single.Single");
        Constructor<Single> con = clazz.getDeclaredConstructor(null);
        con.setAccessible(true);//绕过权限管理,即在true的情况下,可以通过构造函数新建对象
        Single instance = con.newInstance();
        Single instance2 = con.newInstance();
        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());

    }
}

class Single{
    private static boolean flag = false;  
    private Single(){
        synchronized (Single.class) {
            if (!flag) {
                flag = true;
            } else {
                throw new RuntimeException("单例模式被侵犯!");
            }

        }
    }

    private static Single single;

    public static Single getInstance(){
        if ( single == null ) {
            synchronized (Single.class) {
                if ( single == null ) {
                    single = new Single();
                }
            }

        }
        return single;
    }

}

7.总结可以避免序列化反序列化和反射的单例代码

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;

public class Run {
    public static void main(String[] args) throws Exception {
        Single instance = Single.getInstance();
        System.out.println(instance.hashCode());
        System.out.println(copyInstance(instance).hashCode());

        //使用反射方式直接调用私有构造器
        Class<Single> clazz = (Class<Single>)Class.forName("com.lebron.test.single.Single");
        Constructor<Single> con = clazz.getDeclaredConstructor(null);
        con.setAccessible(true);//绕过权限管理,即在true的情况下,可以通过构造函数新建对象
        Single instance1 = con.newInstance();
        Single instance2 = con.newInstance();
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());

    }

    private static Single copyInstance(Single instance) throws Exception{
        FileOutputStream fos = new FileOutputStream("d:/a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
        Single single2 = (Single)ois.readObject();
        oos.close();
        ois.close();
        return single2;
    }
}

class Single implements Serializable{
    private static final long serialVersionUID = 1L;
    private static boolean flag = false;  
    private Single(){
        synchronized (Single.class) {
            if (!flag) {
                flag = true;
            } else {
                throw new RuntimeException("单例模式被侵犯!");
            }

        }
    }

    private static Single single;

    public static Single getInstance(){
        if ( single == null ) {
            synchronized (Single.class) {
                if ( single == null ) {
                    single = new Single();
                }
            }

        }
        return single;
    }

    private Object readResolve() {
        return single;
    }
}

喜欢这篇文章的朋友,欢迎长按下图关注公众号lebronchen,第一时间收到更新内容。
扫码关注

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值