单例模式(Singleton)
确保某个类只有一个实例,并且自行实例化并向整个系统提供这个单例。
单例对象能保证在一个JVM中,该对象只有一个实例存在。好处如下:
(1)某些类的创建比较频繁,对于一些大型的对象,系统开销很大;
(2)省去了new操作符,降低了系统内存的使用频率,减轻GC压力;
单例模式的使用场景
在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现问题,则可采用单例模式,具体的场景如下:
(1)要求生产唯一序列号的环境;
(2)在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都纪录到数据库中,使用单例模式保持计数器的值,并确保线程的安全的。
(3)创建一个对象需要的资源过多,如要访问IO、访问数据库等资源。
(4)需要定义大量静态常量和静态方法(如工具类)的环境,可以采用单例模式
饿汉式(单例实例在类装载的时候就创建)
/**
* 饿汉式,单例实例在类装载的时候就创建
*
*/
public class Singleton1 {
//构造方法私有
private Singleton1() {
}
//在类装载时创建实例
private static Singleton1 instance = new Singleton1();
//获得实例
public Singleton1 getInstance(){
return instance;
}
}
懒汉式(单例实例在第一次被使用时构建,延迟初始化。)
/**
* 懒汉式,单例实例在第一次使用时再创建,延迟初始化
*
*/
public class Singleton2 {
//持有私有静态实例,防止被引用,赋值为null,实现延迟加载
private static Singleton2 instance = null;
//构造方法私有,防止被实例化
private Singleton2() {
}
//创建实例
public static Singleton2 getInstance(){
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
这个类基本可以满足要求,但是在多线程的环境下,会出现问题,解决办法:对getInstance()方法加synchronized关键字。但是synchronized关键字锁住的是这个对象,会降低性能,每次调用getInstance()方法都要对对象上锁,只有在第一次创建对象的时候需要加锁,之后就不需要了。
instance = new Singleton();是分两步来执行的。JVM不保证这两个操作的先后顺序,可能JVM会为新的Singleton实例分配空间,直接赋值给instance成员,然后再初始化这个Singleton实例,这样就可能出错了。
要想实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。但是这样会浪费一定的空间,这种实现方式会在类加载的时候就初始化对象,不管需不需要。
类级内部类(静态内部类),加载外部类的时候,并不会同时加载其静态内部类,只有在发生调用的时候才会进行加载,加载的时候就会创建单例实例并返回,实现了延迟加载;至于同步问题,采用静态初始化器的方式。
实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载时,这个类的加载过程是线程互斥的。当我们第一次调用getInstance()的时候,JVM能够帮我们保证instance只被创建一次,会保证把赋值给instance的内存初始化完毕。
/**
* 使用内部类维护单例的实现
*
*/
public class Singleton3 {
//构造方法私有,防止被实例化
private Singleton3() {
}
//使用内部类来维护实例
private static class SingletonFactory{
private static Singleton3 instance = new Singleton3();
}
//获取实例
public Singleton3 getInstance(){
return SingletonFactory.instance;
}
//如果对象被用于实例化,可以保证对象在序列化前后保持一致
public Object readReaolve(){
return getInstance();
}
}
类的静态方法和单例的区别
(1)静态类不能实现接口。(接口中不允许有static修饰的方法)
(2)单例可以被延迟初始化,静态类一般在第一次加载时初始化。之所以延迟加载,是因为有些类比较庞大,延迟加载有助于提升性能。
(3)单例类可以被继承,他的方法可以被重写,但是静态类内部方法都是static,无法被重写(只有非静态方法才有多态)。
在Spring中,每个Bean都是默认单例的,优点是Spring容器可以管理这些Bean的生命期,决定什么时候创建出来,什么时候销毁,销毁的时候要如何处理等。
参考博客
[1] 单例模式
http://www.cnblogs.com/cbf4life/archive/2009/12/18/1626850.html
[2] Java开发中的23种设计模式详解(转)
http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html