模式导读:
单例模式:保证一个类只有一个实例,并且能够提供一个访问该实例的全局访问点。
优点:由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
参考类图:
代码实现:
常见的五种单例模式实现方式:
主要:
饿汉式:线程安全,调用效率高,不能延时加载。
1 package com.etc; 2 3 //饿汉式单例模式,不可延时加载,调用效率高 4 public class HungrySingleton { 5 // 类初始化时立即加载这个对象,加载类时天然的是线程安全的 6 private static HungrySingleton instance = new HungrySingleton(); 7 8 // 让构造器为 private,这样该类就不会被实例化 9 private HungrySingleton() { 10 } 11 12 // 获取唯一可用的对象,方法没有同步,调用效率高 13 public static HungrySingleton getInstance() { 14 return instance; 15 } 16 17 public void showMessage() { 18 System.out.println("饿汉式单例模式测试输出:立即加载!"); 19 } 20 }
懒汉式:线程安全,调用效率不高,可以延时加载。
1 package com.etc; 2 //懒汉式单例模式 3 public class LazySingleton { 4 //类初始化时,不初始化对象,延时加载 5 private static LazySingleton instance=null; 6 //私有化构造器 7 private LazySingleton() { 8 9 } 10 //设置了方法同步,每次调用该方法都要同步,并发效率低,资源利用率高 11 public synchronized static LazySingleton getInstance() { 12 //当需要使用时再初始化类对象 13 if(instance==null) { 14 instance=new LazySingleton(); 15 } 16 return instance; 17 } 18 public void showMessage() { 19 System.out.println("懒汉式单例模式测试输出:延时加载!"); 20 } 21 }
其他:
双重检测锁式:由于JVM底层内部模型原因,偶尔会出问题,不建议使用。
1 package com.etc; 2 //双重检测锁式单例模式 3 public class DCClockSingleton { 4 5 public static DCClockSingleton instance=null; 6 //私有化构造器 7 private DCClockSingleton() { 8 9 } 10 //将同步内容下放到if内部,提高了执行的效率,不必每次获取对象都进行同步,只有第一次才同步创建了以后就不必同步 11 public static DCClockSingleton getInstance() { 12 if(instance ==null) { 13 DCClockSingleton ds; 14 //第一重检测锁 15 synchronized(DCClockSingleton.class) { 16 ds=instance; 17 if(ds==null) { 18 //第二重检测锁 19 synchronized(DCClockSingleton.class) { 20 if(ds==null) { 21 ds=new DCClockSingleton(); 22 } 23 } 24 instance=ds; 25 } 26 } 27 } 28 return instance; 29 } 30 public void showMessage() { 31 System.out.println("双重检测锁单例模式测试输出:延时加载!"); 32 } 33 }
静态内部类式:线程安全,调用效率高,可以延时加载。
1 package com.etc; 2 //静态内部类单例模式 3 public class StaticNativeClassSingleton { 4 //私有化构造器 5 private StaticNativeClassSingleton() { 6 } 7 //静态内部类,实现类的延时加载 8 private static class InstanceSingleton{ 9 private static final StaticNativeClassSingleton instance=new StaticNativeClassSingleton(); 10 } 11 //方法未同步,效率高 12 public static StaticNativeClassSingleton getInstance() { 13 return InstanceSingleton.instance; 14 } 15 public void showMessage() { 16 System.out.println("静态内部类单例模式测试输出:延时加载!"); 17 18 } 19 }
枚举单例:线程安全,调用效率高,不能延时加载。
1 package com.etc; 2 //枚举单例模式 3 public enum EnumSingleton { 4 //枚举元素本身就是代表单例对象 5 instance; 6 public void showMessage() { 7 System.out.println("枚举单例模式测试输出:立即加载!"); 8 } 9 }
ps:测试类
1 package com.etc; 2 3 public class SingletonTest { 4 5 public static void main(String[] args) { 6 //饿汉式单例模式测试输出 7 HungrySingleton hungry=HungrySingleton.getInstance(); 8 hungry.showMessage(); 9 //懒汉式单例模式测试输出 10 LazySingleton lazy=LazySingleton.getInstance(); 11 lazy.showMessage(); 12 //双重检测锁单例模式测试输出 13 DCClockSingleton dcclock=DCClockSingleton.getInstance(); 14 dcclock.showMessage(); 15 //静态内部类单例模式测试输出 16 StaticNativeClassSingleton sns=StaticNativeClassSingleton.getInstance(); 17 sns.showMessage(); 18 //枚举单例模式测试输出 19 EnumSingleton e=EnumSingleton.instance; 20 e.showMessage(); 21 22 } 23 }
效果截图:
单例模式优缺点:
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2、避免对资源的多重占用。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
适用场景:
1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。