设计模式第六天之单例模式
这次和大家分享一下设计模式中的单例模式。
说到单例模式,我相信大家都了解,简单说就是某个类在整个程序中只有一个对象。那为什么使用单例模式呢?什么场景使用单例模式呢?
我个人认为,在程序设计中,经过分析,某个类有一个对象已经可以满足要求,如果此时再加上这个类会消耗许多资源(包括内存开销大,创建对象耗时等),这个类就推荐使用单例来实现了。比如:数据库连接及相关操作,图片加载,缓存,线程池等等。使用单例模式也可以更好的实现共享资源。
那如何实现单例模式呢?在这里我和大家分享五种方式。在这五种方式中,都是以一个简单的计算器例子来说明,计算加减乘除的类对象明显在程序中只要有一个就可以了。在这里只是为了介绍这些方式实现。
饿汉模式
1 /** 2 * 饿汉模式实现单例模式 3 * 好处:简单 4 * 缺点: 5 * 当类加载的时候就创建了对象而不管对象时候在使用 6 * 造成资源的浪费 7 * 当内存不足的时候,回收了静态对象,此时访问也会带来bug 8 * @author wangpeiyu 9 * 10 */ 11 public class Hungry { 12 private static Hungry instance= new Hungry(); 13 14 private Hungry(){ 15 16 } 17 public Hungry getInstance(){ 18 return instance; 19 } 20 public int add(int a,int b){ 21 return a+b; 22 } 23 public int sub(int a,int b){ 24 return a-b; 25 } 26 public int mul(int a, int b){ 27 return a*b; 28 } 29 public float div(int a,int b) 30 { 31 return a/b; 32 } 33 }
懒汉模式
1 /** 2 * 懒汉实现单例模式 3 * 好处:解决了线程安全,也做到了要使用的时候才创建对象 4 * 缺点:每次要获取该对象的时候,都要进行线程同步,即使在instance已经赋值了之后 5 * 还要进行线程同步,这就造成了不必要的时间浪费和资源浪费 6 * @author wangpeiyu 7 * 8 */ 9 public class Lazy { 10 private static Lazy instance=null; 11 private Lazy(){} 12 public synchronized static Lazy getInstance(){ 13 if(instance==null){ 14 instance = new Lazy(); 15 } 16 return instance; 17 } 18 public int add(int a,int b){ 19 return a+b; 20 } 21 public int sub(int a,int b){ 22 return a-b; 23 } 24 public int mul(int a, int b){ 25 return a*b; 26 } 27 public float div(int a,int b) 28 { 29 return a/b; 30 } 31 }
静态内部类方式
1 /** 2 * 静态内部类实现单例模式 3 * 好处:线程安全、也达到了延迟加载、当加载单例类的时候并没有创建对象。 4 * 缺点:内存不足,回收静态对象时,在访问会出现bug 5 * 无法解决发序列化导致的对象重建 6 * @author wangpeiyu 7 * 8 */ 9 public class StaticInClass { 10 11 private StaticInClass(){ 12 13 } 14 public static StaticInClass getInstance() 15 { 16 return SingletonHolder.instance; 17 } 18 /** 19 * 静态内部类 20 * 主要的单例类的holder 21 * @author wangpeiyu 22 * 23 */ 24 private static final class SingletonHolder{ 25 private static final StaticInClass instance = new StaticInClass(); 26 } 27 public int add(int a,int b){ 28 return a+b; 29 } 30 public int sub(int a,int b){ 31 return a-b; 32 } 33 public int mul(int a, int b){ 34 return a*b; 35 } 36 public float div(int a,int b) 37 { 38 return a/b; 39 } 40 }
双重检查加锁方式
1 /** 2 * 双重检查加锁模式实现单例模式 3 * 好处:延迟了加载、线程安全,而且也避免了在instance已经赋值的情况下,多线程还要 4 * 同步造成的资源浪费和无意义的等待 5 * 缺点:当反序列化时无法避免会重新创建对象的问题 6 * @author wangpeiyu 7 * 8 */ 9 public class DCL { 10 private static DCL instance = null; 11 private DCL(){} 12 public static DCL getInstance(){ 13 if(instance==null){ 14 synchronized (DCL.class) { 15 if(instance==null) 16 { 17 instance = new DCL(); 18 } 19 } 20 } 21 return instance; 22 } 23 public int add(int a,int b){ 24 return a+b; 25 } 26 public int sub(int a,int b){ 27 return a-b; 28 } 29 public int mul(int a, int b){ 30 return a*b; 31 } 32 public float div(int a,int b) 33 { 34 return a/b; 35 } 36 }
枚举方式
1 /** 2 * 使用枚举实现单例 3 * 好处: 4 * 1.完全确保整个程序只有一个实例 5 * 2.线程安全,因为枚举一旦定义结束,编译器就不允许我们再使用其构造器来创建任何实例了 6 * 3.构造函数是private的,即使定义为public,一样为private 7 * 4.自由序列化,这样即使在反序列化时也不会重新创建 8 * @author wangpeiyu 9 * 10 */ 11 public enum EnumCaculate { 12 instance; 13 private EnumCaculate(){ 14 } 15 public int add(int a,int b){ 16 return a+b; 17 } 18 public int sub(int a,int b){ 19 return a-b; 20 } 21 public int mul(int a, int b){ 22 return a*b; 23 } 24 public float div(int a,int b) 25 { 26 return a/b; 27 } 28 }
在这五种实现中,我个人比较推荐枚举实现和双重检查加锁实现。原因在每个类的代码中体现了。

浙公网安备 33010602011771号