1:单例模式简介
单例模式是一种常用的软件设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供唯一的实例。总而言之就是在系统中只会存在一个对象,其中的数据是共享的
特点:
单例类只能有一个实例,所以一般会用static进行修释。
单例类必须自己创建自己的唯一实例。也就是在类中要new一个自己。
单例类必须向其它对象提供自己的实例。也就是要提供公共的方法获取到自己的实例对象。
线程安全问题:
在使用单例对象的时候,要注意单例对象里面的实例变量是会被多线程共享的,推荐将单例对象里面的实例变量使用无状态的对象,不会因为多个线程的交替调度而破坏自身状态导致线程安全问题。
2:实现单例模式的方式
(1)饿汉式单例(立即加载模式)
1 1 //单例类--饿汉式 2 2 public class Singleton{ 3 3 //私有的构造方法,禁止外部对其进行实例化 4 4 private Singleton(){}; 5 5 //用static修饰唯一,这里只要Singleton类加载初始化时会new出自己的实例 6 6 private static Singleton single=new Singleton(); 7 7 //公共的获取对象方法,在此程序中只能通过getInstance方法来获取Singleton实例 8 8 public static Singleton getInstance(){ 9 9 return single; 1010 } 1111 }
因为这里只要Singleton类加载的时候就创建了对象,所以不会存在线程安全问题。
(2)懒汉式单例(延迟加载模式)
1 1 //单例类--懒汉式(原始设计方式) ps:这样可能会出现线程不安全,导致多实例 2 2 public class Singleton1{ 3 3 //私有构造方法,禁止外部对其进行实例化 4 4 private Singleton(){}; 5 5 //用static修饰唯一,这里只是声明了single对象等于null,并没有对其进行实例化 6 6 private static Singleton single=null; 7 7 //公共的获取对象方法 8 8 public static Singleton getInstance(){ 9 9 //只有在获取的时候,而且single没有实例化时才进行实例化 1010 if(single==null){ 1111 single=new Singleton1(); 1212 } 1313 14 15 return single; 1416 } 1517 } 16 17 1 //单例类--懒汉式 18 2 public class Singleton1{ 19 3 //私有构造方法,禁止外部对其进行实例化 20 4 private Singleton(){}; 21 5 //用static修饰唯一,这里只是声明了single对象等于null,并没有对其进行实例化 22 6 private static Singleton single=null; 23 7 //公共的获取对象方法 24 8 public static Singleton getInstance(){ 25 9 //只有在获取的时候,而且single没有实例化时才进行实例化 2610 if(single==null){ //2:如是没有这个判断会导致一个线程获取single必须要等待上一个线程释放锁才能获取到对象 2711 synchronized(Singleton1.class){ //1:同步锁,解决多个实例问题 2812 if(single==null){ 2913 single=new Singleton1(); 3014 } 3115 } 3216 } 3317 return single; 3418 } 19 }
1:同步锁:如果不实现同步的话,会导致两个线程同时运行到if(single==null)的时候,两个线程同时满足条件,都会new一个实例对象,导致会出现多个实例问题。
2:如果不加这个判断会导致效率比较低下,一个线程获取single必须要等待上一个线程释放锁才能获取到对象。
使用双重检查进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率
(3)静态内部类
1 1 public class Singleton2{ 2 2 //私有的构造方法 3 3 private Singleton2(){}; 4 4 5 5 //静态内部类 6 6 private static class InnerObject{ 7 7 private static Singleton2 single=new Singleton2(); 8 8 } 9 9 1010 public Singleton2 getInstance(){ 1111 return InnerObject.single; 1212 } 1313 }
这种方法虽然单例在多线程并发的情况下的线程安全性,但在遇到序列化对象时,在默认的方式运行得到的结果就是多例的,使用的时候需要注意。
(4)静态代码块
1public class Singleton3{ 2 //私有构造方法 3 private Singleton3(){} 4 private static Singleton3 singleton=null; 5 6 //静态代码块 7 static{ 8 singleton=new Singleton3(); 9 } 10 public static Singleton3 getInstance(){ 11 return singleton; 12 } 13}
这种方式和饿汉模式是差不多的,因为类加载完静态成员变量后会马上加载类的静态代码块
(5)内部枚举类实现
1public class SingletonFactory { 2 3 // 内部枚举类 4 private enum EnmuSingleton{ 5 Singleton; 6 private Singleton8 singleton; 7 8 //枚举类的构造方法在类加载是被实例化 9 private EnmuSingleton(){ 10 singleton = new Singleton8(); 11 } 12 public Singleton8 getInstance(){ 13 return singleton; 14 } 15 } 16 public static Singleton8 getInstance() { 17 return EnmuSingleton.Singleton.getInstance(); 18 } 19} 20 21class Singleton8{ 22 public Singleton8(){} 23}
比较复杂,感觉在实际开发中也很少有人这样用。
3:优缺点
优点:系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点:当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。
4:适用的场景
需要频繁的创建和销毁的对象。
创建对象耗时过多或者耗费资源过多,但又经常用到的对象。
工具类对象
频繁访问数据库或文件对象