单例模式是一种对象建立模式,用于生产对象的一个具体实例,它能够确保系统中一个类只产生一个实例。该模式能带来两大好处: java
1. 对于频繁使用的对象,能够省略建立对象所花费的时间。 多线程
2. 因为new的次数减小,于是对系统内存的使用频率也会下降,这将减轻GC压力、缩短gc停顿时间 函数
单例模式的核心,在于经过一个接口返回惟一的对象实例。一个简单的单例实现: 工具
public class Singleton { private Singleton(){ System.out.println("create a Singleton"); } private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } }首先,单例必须有一个private访问级别的构造函数,只有这样,才能确保单例不会在系统中的其余代码内被实例化;其次,instance成员变量和getInstance()的方法必须是static的
这种实现方式很简单而且仍是十分可靠的,它惟一的不足就是没法对instance进行延迟加载,实现延迟的代码为 性能
public class LazySingleton { private LazySingleton(){ System.out.println("LazySingleton is create"); } private static LazySingleton instance = null; public static synchronized LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }
首先,对于静态变量instance初始值赋予null,确保系统启动时没有额外的负载;
其次,get方法必须是同步的,不然在多线程请扩下,可能会屡次建立!
可是因为引入了同步,其性能会和第一种状况差了不少,因此这种下降了系统性能的作法,是有点不可取的,因此咱们对其进行改进:
public class StaticSingleton { private StaticSingleton(){ System.out.println("create a StaticSingleton"); } private static class SingletonHolder{ private static StaticSingleton instance = new StaticSingleton(); } public static StaticSingleton getInstance(){ return SingletonHolder.instance; } }
这个实现中,单例模式使用内部类来维护实例,当StaticSingleton被加载时,其内部类不会初始化,故能够在调用getInstance()方法调用,才会加载SingletonHolder.同时,因为实例的创建实在类加载时完成,故天生对多线程友好
一般状况下上面的代码能够确保系统中惟一实例。可是仍有意外状况,好比利用反射,这种状况咱们不讨论,咱们讨论一种合理的方法:
public class SerSingleton implements Serializable { String name; private SerSingleton(){ System.out.println("create a SerSingleton"); name = "SerSingleton"; } private static SerSingleton instance = new SerSingleton(); public static SerSingleton getInstance(){ return instance; } public static void createString(){ System.out.println("createString in Singleton"); } // private Object readResolve(){ // return instance; // } }
// TODO Auto-generated method stub SerSingleton s1 = null; SerSingleton s = SerSingleton.getInstance(); //先将实例串行化到文件 FileOutputStream fos = new FileOutputStream("s.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s); oos.flush(); oos.close(); //从文件读出 FileInputStream fis = new FileInputStream("s.txt"); ObjectInputStream ois = new ObjectInputStream(fis); s1= (SerSingleton) ois.readObject(); // Assert.assertEquals(s,s1); System.out.println(s.equals(s1));
若是在第一段代码中去掉readResovle(),那么测试代码(通过串行化和反串行化)后,s和s1指向了不一样的实例;加上以后便解决该问题。
即便构造函数是私有的,可序列化工具依然能够经过特殊的途径去建立类的一个新的实例。序列化操做提供了一个很特别的钩子(hook)-类中具备一个私有的被实例化的方法readresolve(),这个方法能够确保类的开发人员在序列化将会返回怎样的object上具备发言权。足够奇怪的,readresolve()并非静态的,可是在序列化建立实例的时候被引用。