单例模式算是设计模式中比较简单,还有大家接触的早的设计模式之一了,单例模式分为恶汉式单例模式和懒汉式单例模式。
一、恶汉式单例
/** * 恶汉式单例 */ public class Singleton { private static final Singleton singleton = new Singleton(); private Singleton() {} public static Singleton createSignleton(){ return singleton; } }
测试:
//恶汉式单列测试 Singleton singleton = Singleton.createSignleton(); Singleton singleton1 = Singleton.createSignleton(); System.out.println("singleton == singleton1:" + (singleton == singleton1));
输出:
在一开始就创建实例,这样的单例模式在类一加载的时候就开始创建了,如果长时间不用,就会有种浪费资源的感觉,所以最好是在需要的时候再创建,由此出现了懒汉式单例。
二、懒汉式单例
/** * 懒汉式单列 */ public class SingletonA { private static SingletonA singletonA = null; private static final Object syncFlag = new Object(); private SingletonA(){ } public static SingletonA createSingletonA(){ if (singletonA == null) { singletonA = new SingletonA(); } return singletonA; } }
在需要的时候进行创建、
测试:
//懒汉式单列测试 SingletonA singletonA = SingletonA.createSingletonA(); SingletonA singletonA1 = SingletonA.createSingletonA(); System.out.println("singletonA == singletonA1:" + (singletonA == singletonA1));
输出:
这种创建在多线程情况下会出现创建的两个单例对象不相等,所以在创建的时候加上线程锁,保证线程安全,由此出现了线程安全的懒汉式单例:
三、线程安全的懒汉式单例
public class SingletonA { private static SingletonA singletonA = null; private SingletonA(){ } public static SingletonA createSingletonA(){ if (singletonA == null) { synchronized (SingletonA.class){ //使用双重锁定 保证线程安全 if (singletonA == null) { singletonA = new SingletonA(); } } } return singletonA; } }
如果只有外层一个空判断还是有可能会出现线程不安全,所以双重判断定更能保护线程安全。由于添加了synchronized(会造成线程阻塞)性能上还是多少会有点损耗,(synchronized也可以加在方法上,这样性能上更差)那能不能把synchronized去掉呢?所以又出现了一种单例,内部类形式的懒汉式单例。
四、内部类形式的懒汉式单例
public class SingletonB { private SingletonB() { } static class InternalSingletonB{ private static final SingletonB singletonB = new SingletonB(); } public static SingletonB createSingletonB(){ return InternalSingletonB.singletonB; } }
因为单例对象实例化是在内部类加载的时候构建的,所以是线程安全的(在方法中创建对象,会存在并发问题,而静态内部类是随着方法调用而被加载,只加载一次,不会存在并发问题,所以是线程安全的),而且在createSingletonB()方法上没有添加synchronized关键字,所以不会造成性能上的损耗,当我们调用SingletonB.createSingletonB()创建对象的时候,才去加载内部类InternalSingletonB。由此来达到懒加载的目的。
测试:
//懒汉式单列测试B内部类情况 SingletonB singletonB = SingletonB.createSingletonB(); SingletonB singletonB1 = SingletonB.createSingletonB(); System.out.println("singletonB == singletonB1:"+ (singletonB == singletonB1));
输出:
这个版本的单例在利用反射的时候会出现两个对象不相的情况:
测试:
//利用反射尝试 懒汉式单列测试B内部类情况 SingletonB singletonB2 = SingletonB.createSingletonB(); SingletonB singletonB3 = null; Class<SingletonB> aClass = SingletonB.class; try { Constructor<SingletonB> constructor = aClass.getDeclaredConstructor(); constructor.setAccessible(true); singletonB3 = constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } System.out.println("singletonB2哈希值:" + singletonB2.hashCode()); System.out.println("singletonB3哈希值:" + singletonB3.hashCode()); System.out.println("singletonB2=singletonB3:" + (singletonB2 == singletonB3));
输出:
由此又出现了一个单例:
五、内部类形式的懒汉式单例(处理反射之后不相等的情况)
public class SingLetonC { private static boolean isInit = false; private SingLetonC() { //防止单例被破坏 if (isInit) { throw new RuntimeException("单例已经被破坏"); } isInit = true; } static class InternalSingletonC { private static final SingLetonC singletonC = new SingLetonC(); } public static SingLetonC createSingletonC() { return InternalSingletonC.singletonC; } }
测试:
//利用反射尝试 懒汉式单列测试B内部类情况 SingLetonC singletonC2 = SingLetonC.createSingletonC(); SingLetonC singLetonC3 = null; Class<SingLetonC> cClass = SingLetonC.class; try { Constructor<SingLetonC> constructor = cClass.getDeclaredConstructor(); constructor.setAccessible(true); //创建对象时候单列已经被破坏 singLetonC3 = constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } System.out.println("singletonC2哈希值:" + singletonC2.hashCode()); System.out.println("singletonC3哈希值:" + singLetonC3.hashCode());
输出:
这样达到了防止单例被破坏的情况(不用反射创建就行),但是这种单例模式在系列化对象的时候又会出现不相等的问题,先实现系列化接口:
public class SingLetonC implements Serializable { private static final long serialVersionUID = -4827468642694587697L; private static boolean isInit = false; private SingLetonC() { //防止单例被破坏 if (isInit) { throw new RuntimeException("单例已经被破坏"); } isInit = true; } static class InternalSingletonC { private static final SingLetonC singletonC = new SingLetonC(); } public static SingLetonC createSingletonC() { return InternalSingletonC.singletonC; } }
测试:
SingLetonC singLetonC4 = SingLetonC.createSingletonC(); ObjectOutput objectOutput = null; try { objectOutput = new ObjectOutputStream(new FileOutputStream("file.ser")); objectOutput.writeObject(singLetonC4); objectOutput.close(); ObjectInput objectInput = new ObjectInputStream(new FileInputStream("file.ser")); SingLetonC singLetonC5 = (SingLetonC) objectInput.readObject(); objectInput.close(); System.out.println(" singLetonC4 hashCode = " + singLetonC4.hashCode()); System.out.println(" singLetonC5 hashCode = " + singLetonC5.hashCode()); System.out.println(" singLetonC4 = singLetonC5:" + (singLetonC4 == singLetonC5)); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }
输出:
同一个对象系列化之后变成了2个不相等的对象。要保证系列化之后2个对象还相等的话需要添加readResolve()(这个方法是反系列化读取的时候使用)由此进行了改进(改进版单例):
public class SingLetonC implements Serializable{ private static final long serialVersionUID = -4827468642694587697L; private static boolean isInit = false; private SingLetonC(){ //防止单例被破坏 if(isInit) { throw new RuntimeException("单例已经被破坏"); } isInit = true; } static class InternalSingletonC{ private static final SingLetonC singletonC = new SingLetonC(); } public static SingLetonC createSingletonC(){ return InternalSingletonC.singletonC; } /** * 提供该方法的实现。readResolve()代替了从流中读取对象 * 这就确保了在序列化和反序列化的过程中没人可以创建新的实例。 * 这个方法可以确保类的序列化将会返回怎样的object。 * readresolve()并不是静态的,在序列化创建实例的时候被引用 * * @return */ private Object readResolve(){ return createSingletonC(); } }
测试代码还是和上面一样:
输出: