目录
1.使用单例模式的作用
2.常见的应用场景
3.单例模式的实现方式
4.单例模式存在的问题及解决办法
5.总结
一、使用单例模式的作用
单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
二、常见的应用场景
1.Windows的任务管理器
2.Windows的回收站
3.操作系统的文件系统
4.Servlet 中的Application作用域
5.Spring中的每个Bean都是单例
6.Servlet中每个Servlet都是单例
三、单例模式的实现方式
常见的五种实现方式:
1.饿汉式(线程安全,调用效率高。但是不能延迟加载)
2.懒汉式(线程安全,调用效率不高,但是可以延迟加载)
3.双重检测锁式(由于JVM底层内部原因,偶尔会出现问题,不建议使用)
4.静态内部类式(线程安全,调用效率高,可以延迟加载)
5.枚举单例(线程安全,调用效率高,不能延迟加载)
1、饿汉式
1 package com.demo.singleton; 2 /** 3 * 饿汉式单例设计模式实现 4 */ 5 public class Singleton1 { 6 7 private static Singleton1 singleton = new Singleton1(); 8 //私有化构造方法 9 private Singleton1(){} 10 public static Singleton1 getSingleton(){ 11 return singleton; 12 } 13 }
2、懒汉式
1 package com.demo.singleton; 2 /** 3 * 懒汉式单例设计模式实现 4 */ 5 public class Singleton2 { 6 7 private static Singleton2 singleton; 8 //私有化构造方法 9 private Singleton2(){} 10 public synchronized static Singleton2 getSingleton(){ 11 if(singleton==null){ 12 singleton = new Singleton2(); 13 } 14 return singleton; 15 } 16 }
3、双重检测锁(不推荐)
1 package com.demo.singleton; 2 /** 3 * 双重检测锁实现单例设计模式 4 */ 5 public class Singleton3 { 6 7 private static Singleton3 singleton=null; 8 //私有化构造方法 9 private Singleton3(){} 10 public static Singleton3 getSingleton(){ 11 if(singleton==null){ 12 Singleton3 sc; 13 synchronized(Singleton3.class){ 14 sc = singleton; 15 if(sc == null){ 16 synchronized(Singleton3.class){ 17 if(sc == null){ 18 sc = new Singleton3(); 19 } 20 } 21 singleton=sc; 22 } 23 } 24 } 25 return singleton; 26 } 27 }
4、静态内部类实现方式
1 package com.demo.singleton; 2 /** 3 * 静态内部类实现单例设计模式 4 */ 5 public class Singleton4 { 6 private static class singleton{ 7 private static final Singleton4 singleton= new Singleton4(); 8 } 9 private Singleton4(){} 10 public static Singleton4 getSingleton(){ 11 return singleton.singleton; 12 } 13 }
5、使用枚举实现单例模式
1 package com.demo.singleton; 2 /** 3 * 使用枚举实现单例模式 4 */ 5 public enum Singleton5 { 6 /** 7 * 定义一个枚举元素,这个枚举元素本身就代表着一个单例 8 */ 9 SINGLETON; 10 //定义这个单例的操作 11 public Singleton5 getSingleton(){ 12 return SINGLETON; 13 } 14 }
四、单例模式出存在的问题及解决办法
问题:
1.反射可以破解上面几种单例模式(不包含枚举式)
package com.demo.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 利用反射破解Singleton2单例 * @author Administrator * */ public class Client { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //不使用反射破解的情况下 Singleton2 s1 = Singleton2.getSingleton(); Singleton2 s2 = Singleton2.getSingleton(); System.out.println(s1==s2); //使用反射破解 Class<Singleton2> clazz = (Class<Singleton2>) Class.forName("com.demo.singleton.Singleton2"); Constructor<Singleton2> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Singleton2 i1 = constructor.newInstance(); Singleton2 i2 = constructor.newInstance(); System.out.println(i1==i2); } }
解决方案:
在Singleton2类中的私有化构造函数中判断是否存在这个对象,如果存在则手动抛出一个异常。
package com.demo.singleton; /** * 懒汉式单例设计模式实现 */ public class Singleton2 { private static Singleton2 singleton; //私有化构造方法 private Singleton2() throws Exception{ if(singleton!=null){ throw new Exception("只能创建一个实例"); } } public synchronized static Singleton2 getSingleton() throws Exception{ if(singleton==null){ singleton = new Singleton2(); } return singleton; } }
2.反序列化可以破解上面几种单例模式(不包含枚举式)
package com.demo.singleton; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * 使用反序列化破解单例模式 * @author Administrator * */ public class Client2 { public static void main(String[] args) throws Exception { //序列化单例类 Singleton2 s =Singleton2.getSingleton(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.obj")); oos.writeObject(s); oos.flush(); oos.close(); //反序列化单例 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.obj")); Singleton2 s2 = (Singleton2)ois.readObject(); ois.close(); System.out.println(s==s2); } }
解决方案:
在序列化的方法里面定义一个readResolve()方法,在反序列化的时候会回调这个函数。
package com.demo.singleton; import java.io.ObjectStreamException; import java.io.Serializable; /** * 懒汉式单例设计模式实现 */ public class Singleton2 implements Serializable{ private static Singleton2 singleton; //私有化构造方法 private Singleton2() throws Exception{ if(singleton!=null){ throw new Exception("只能创建一个实例"); } } public synchronized static Singleton2 getSingleton() throws Exception{ if(singleton==null){ singleton = new Singleton2(); } return singleton; } private Object readResolve() throws ObjectStreamException{ return singleton; } }
五、总结
单例模式的优点:
——由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动
时直接产生一个单例对象,然后永久驻留内存的方式来解决
——单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
具体使用哪种单例模式:
– 单例对象 占用 资源 少,不需要 延时加载:
• 枚举式 好于 饿汉式
– 单例对象 占用 资源 大,需要 延时加载:
• 静态内部类式 好于 懒汉式