单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
思路是通过将该类的构造方法设为private,那么在其他类中不能直接实例化该类。那么为了得到该类的实例,需要有public的static方法来返回该类的实例。(之所以static是为了可以直接用类名来调用该方法,不然的话没有该类的实例,无法调用该类的非static方法)
单例模式有以下特点: 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。
单例模式主要有以下三种实现方式:懒汉式单例、饿汉式单例、登记式单例。下面来看代码示例:
一、懒汉式单例
[java] view plain copy //懒汉式单例类.在第一次调用的时候实例化自己 public class Singleton { private Singleton() {} private static Singleton single=null; //静态工厂方法 public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } }
懒汉式其实是一种比较形象的称谓。既然懒,那么在创建对象实例的时候就不着急。会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推脱不开的时候才会真正去执行工作,因此在装载对象的时候不创建对象实例。懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。
二、饿汉式单例
[java] view plain copy //饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 { private Singleton1() {} private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() { return single; } }
饿汉式也是一种比较形象的称谓。既然饿,那么在创建对象实例的时候就比较着急,饿了嘛,于是在装载类的时候就创建对象实例。饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
三、登记式单例[java] view plain copy //类似Spring里面的方法,将类名注册,下次从里面直接获取。 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()); } } 登记式单例 实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。
线程安全问题
上文中懒汉式单例的代码是线程不安全的。为了线程安全,可以采用以下方法:
1、在getInstance方法上加synchronized
[java] view plain copy public static synchronized Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } 2、双重检查锁定
[java] view plain copy public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } 3、静态内部类
[java] view plain copy public class Singleton { private Singleton(){} /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 * 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。 */ private static class SingletonHolder{ /** * 静态初始化器,由JVM来保证线程安全 */ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.instance; } }
当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
另外就静态内部类中的三个static说明如下:
[java] view plain copy public static Singleton getInstance(){ return SingletonHolder.instance;}中的static是为了可以用类名直接调用getInstance方法,所以必须为static。那么该方法内的成员变量也必须为static,否则那个成员变量就要用类的实例来访问了,这显然和通过类直接访问不相符。
[java] view plain copy private static Singleton instance = new Singleton();中的static是因为instance必须为static才可以在getInstance方法中出现。
而
[java] view plain copy public static Singleton getInstance()中的static是因为若内部类中出现静态成员变量,则该内部类必为静态内部类。
最后来看一个单例模式应用的例子有助于理解:
[java] view plain copy public class TMain{ public static void main(String[] args){ Singleton s1=Singleton.getInstance(); s1.setName("libai"); Singleton s2=Singleton.getInstance(); s2.setName("dufu"); System.out.println(s1.getName()); System.out.println(s2.getName()); if(s1==s2){ System.out.println("是同一个对象"); }else{ System.out.println("不是同一个对象"); } } } class Singleton{ /* 私有构造方法,防止被实例化 */ private Singleton() { } private String name; public void setName(String s){ this.name=s; } public String getName(){ return this.name; } /*饿汉式单例*/ private static final Singleton instance = new Singleton(); /* 获取实例 */ public static Singleton getInstance() { //return new Singleton(); return instance; } /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */ public Object readResolve() { return getInstance(); } }
输出为:
[java] view plain copy dufu dufu 是同一个对象 原文链接:http://blog.csdn.net/sinat_26888717/article/details/48226365
