六种方式实现单例模式:
Java经典设计模式共有21中,分为三大类:创建型模式(5种)、结构型模式(7种)和行为型模式(11种)。
单例模式,最简单又最常用的设计模式,即只创建一个实例对象。下面给大家介绍用六种方式实现单例模式,以及各种方式的利弊,并附上代码...
1.饿汉式
优点:简单易用,调用效率高,线程安全
缺点:没有延时加载的优势,类初始化时立即加载这个对象(有可能加载类时并不需要此对象,所以可能浪费内存)
public class SingletonDemo1 { //类初始化时立即加载这个对象(没有延时加载的优势),由于加载类时,天然是线程安全的 private static SingletonDemo1 instance = new SingletonDemo1(); //私有化构造器 private SingletonDemo1(){} //没有同步,调用效率高 public static SingletonDemo1 getInstance (){ return instance; } }
2.懒汉式
优点:类初始化时不加载对象(延时加载,真正调用对象时才创建)缺点:方法需要同步,调用效率低
public class SingletonDemo2 { //类初始化时,不初始化对象(延时加载,真正用的时候再创建)。 private static SingletonDemo2 instance; //私有化构造器 private SingletonDemo2(){} //方法同步,调用效率低 public static synchronized SingletonDemo2 getInstance(){ if(instance == null){ instance = new SingletonDemo2(); } return instance; } }
3.双重检测锁
说明:此方式可能运行时可能会有问题,了解就好
public class SingletonDemo3 { private static SingletonDemo3 instance = null; private SingletonDemo3(){} public static SingletonDemo3 getInstance(){ if(instance == null){ SingletonDemo3 sc; synchronized (SingletonDemo3.class) { sc = instance; if(sc == null){ synchronized (SingletonDemo3.class) { if(sc == null){ sc = new SingletonDemo3(); } } } instance = sc; } } return instance; } }
4.静态内部类
说明:此方式调用掉率高,线程安全,并且实现了延时加载,建议使用
public class SingletonDemo4 { private static class SingletonClassInstance { private static final SingletonDemo4 instance = new SingletonDemo4(); } private SingletonDemo4 (){} //方法没有同步,调用效率高 public static SingletonDemo4 getInstance(){ return SingletonClassInstance.instance; } }
5.枚举方式
优点:调用效率高,天然线程安全,天然单例缺点:没有延时加载
public enum SingletonDemo5 { //这个枚举元素,本身就是单例对象 INSTANCE; //添加自己的操作 public void singletonOperation(){ } }
6.单例模式如何防止反射和序列化反序列化漏洞
反射漏洞:通过反射可以调用已经被私有化的构造器,从而创建新的对象序列化反序列化漏洞:通过将对象序列化永久存入磁盘中,再反序列化取出对象,此时已经不是同一个对象
(1)破解单例模式相关代码<以懒汉式为例>(2)防止单例模式漏洞代码 <以懒汉式为例>public class Client2 { public static void main(String[] args) throws Exception{ SingletonDemo6 s1 = SingletonDemo6.getInstance(); SingletonDemo6 s2 = SingletonDemo6.getInstance(); System.out.println(s1); System.out.println(s2); //通过反射破解单例模式 // Class<SingletonDemo6> c = (Class<SingletonDemo6>) Class.forName("org.exx.study.singleton.SingletonDemo6"); // Constructor<SingletonDemo6> constructor = c.getDeclaredConstructor(null); // constructor.setAccessible(true); // // SingletonDemo6 s3 = constructor.newInstance(); // SingletonDemo6 s4 = constructor.newInstance(); // // System.out.println(s3); // System.out.println(s4); //通过反序列化破解单例模式 FileOutputStream fos = new FileOutputStream("G:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); fos.close(); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("G:/a.txt")); SingletonDemo6 s3 = (SingletonDemo6) ois.readObject(); System.out.println(s3); } }
public class SingletonDemo6 implements Serializable{ //类初始化时,不初始化对象(延时加载,真正用的时候再创建)。 private static SingletonDemo6 instance; //私有化构造器 private SingletonDemo6(){ /** * 此段代码防止反射漏洞 */ if(instance != null){ throw new RuntimeException(); } } //方法同步,调用效率低 public static synchronized SingletonDemo6 getInstance(){ if(instance == null){ instance = new SingletonDemo6(); } return instance; } /** * 次方法用来防止序列化反序列化漏洞 * 反序列化时,如果定义了readResolve方法,则直接返回方法指定的对象,而不需要再创建新对象 * @return * @throws ObjectStreamException */ private Object readResolve() throws ObjectStreamException{ return instance; } }
7.以上几种方式的效率测试
public class Client3 { public static void main(String[] args) throws InterruptedException { /******测试时间**********/ //饿汉式:8 //懒汉式:1702 //双重检测锁:19 //内部静态类:41 //枚举:6 long start = System.currentTimeMillis(); int threadNum = 10; final CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000000; i++) { Object o = SingletonDemo5.INSTANCE; } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); //main线程阻塞,知道线程计数器为0,才会继续向下执行 long end = System.currentTimeMillis(); System.out.println("总耗时:"+(end-start)); } }