Java中,单例模式通常有2种分类饿汉模式和懒汉模式。
饿汉模式指的是单例实例在类装载时就被创建了。
懒汉方式值的是单例实例在首次使用时才被创建。
无论是饿汉模式还是懒汉模式,都是用了一个静态成员变量来存放真正的实例。并且私有化构造函数,防止被外部实例化。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
懒汉式
public class Singleton { private static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { mInstance = new Singleton(); } return mInstance; } }
懒汉式、线程安全
public class Singleton { private static Singleton mInstance; private Singleton() { } public static synchronized Singleton getInstance() { if (mInstance == null) { mInstance = new Singleton(); } return mInstance; } }
双重检查
public class Singleton { private static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { //保证了同一时间只能只能有一个对象访问此同步块 synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } }
双重检查加volitale
public class Singleton { //添加volatile关键字修饰 private volatile static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { //保证了同一时间只能只能有一个对象访问此同步块 synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } }
饿汉式
public class Singleton { private Singleton() { } private static Singleton mInstance = new Singleton(); public static Singleton getInstance() { return mInstance; } }
静态内部类
public class Singleton { private Singleton() { } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
枚举
public enum Singleton { INSTANCE; Singleton() { } public void doSomething() { //do something... } }
目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次。
登记式单例
public class Singleton3 { private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); static{ Singleton3 single = new Singleton3(); map.put(single.getClass().getName(), single); } //保护的默认构造子 protected Singleton3(){} //静态工厂方法,返还此类惟一的实例 public static Singleton3 getInstance(String name) { if(name == null) { name = Singleton3.class.getName(); System.out.println("name == null"+"--->name="+name); } if(map.get(name) == null) { try { map.put(name, (Singleton3) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); } //一个示意性的商业方法 public String about() { return "Hello, I am RegSingleton."; } public static void main(String[] args) { Singleton3 single3 = Singleton3.getInstance(null); System.out.println(single3.about()); } }
单例例模式的破解与防止.
反射获取对象实例: 通过在私有构造器中添加判断避免
正反序列化获取对象实例:通过添加 readResolve() 可以避免