一.单例模式介绍
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,有利于协调整个系统。一些很消耗资源的实例应该保证只有一个,没有理由让它构造多个实例。这种不能自由构造对象的情况,就是单例模式。
二.单例模式的定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
三.单例模式的使用场景
确保某个类有且只有一种使用场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。
四.单例模式的使用要点
(1)构造函数不对外开放,一般为Privace
(2)通过一个静态方法或者枚举返回单例类对象
(3)确保单例类的对象有且只有一个,尤其是在多线程环境下
(4)确保单例类对象在反序列化时不会重新构造对象
通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式手动构造单例类的对象,单例类会暴露一个共有静态方法,客户端需要调用这个静态方法获取单例类的唯一对象,在获取这个单例对象的过程中确保线程安全,即在多线程环境下构造单例类的对象也是有且只有一个。
五.示例
1.饿汉单例模式
/**
* 饿汉单例模式
*/
public class HungryModeSingleton {
private static final HungryModeSingleton hungryModeSingleton=new HungryModeSingleton();
/**
* 构造方法私有
*/
private HungryModeSingleton() {
}
/**
* 对外暴露的获取方法
* @return 单例对象
*/
public static HungryModeSingleton getInstance(){
return hungryModeSingleton;
}
public void get(){
Log.d("HungryModeSingleton", "这就是饿汉单例模式");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HungryModeSingleton.getInstance().get();
}
}
饿汉式单例模式的特点是在声明对象时就已经初始化。
2.懒汉单例模式
/**
* 懒汉单例模式
*/
public class LazyModeSingleton {
private static LazyModeSingleton lazyModeSingleton;
/**
* 构造方法私有
*/
private LazyModeSingleton(){
}
/**
* 对外暴露的获取方法
* @return 单例对象
*/
public static synchronized LazyModeSingleton getInstance(){
if (null==lazyModeSingleton){
lazyModeSingleton=new LazyModeSingleton();
}
return lazyModeSingleton;
}
public void get(){
Log.d("LazyModeSingleton", "这就是懒汉单例模式");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LazyModeSingleton.getInstance().get();
}
}
synchronized关键字可以在多线程情况下保证对象唯一性的手段。但每次调用getInstance方法都会进行同步,会消耗不必要的资源。
懒汉单例模式的优点是只有在使用时才会被实例化,在一定程度上节省资源;缺点是第一次加载时需要及时实例化,反应稍慢,同时有不必要的同步开销。
3.双重检查锁定单例模式(Double Check Lock)
/**
* 双重检查锁定单例模式
*/
public class DoubleCheckLockSingleton {
private static DoubleCheckLockSingleton doubleCheckLockSingleton=null;
/**
* 构造方法私有
*/
private DoubleCheckLockSingleton() {
}
/**
* 对外暴露的获取方法
* @return 单例对象
*/
public static DoubleCheckLockSingleton getInstance() {
if (null == doubleCheckLockSingleton) {
synchronized (DoubleCheckLockSingleton.class) {
if (null == doubleCheckLockSingleton) {
doubleCheckLockSingleton = new DoubleCheckLockSingleton();
}
}
}
return doubleCheckLockSingleton;
}
public void get(){
Log.d("DoubleCheckLock", "这就是双重检查锁定单例模式");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DoubleCheckLockSingleton.getInstance().get();
}
}
DCL的优点是资源利用率高,第一次执行getInstance时单例对象才会被实例化。缺点是第一次加载反应稍慢,也由于Java内存模型的原因会偶尔失败。在高并发环境下也有一定的缺陷。
而在JDK1.5之后出现了volatile关键字,解决DCL失效问题,但volatile或多或少会影响到性能。
private volatile static DoubleCheckLockSingleton doubleCheckLockSingleton=null;
DCL模型是使用最多的单例实现方式,它能够在需要时才实例化对象,并且能够在绝大多数场景下保证单例对象的唯一性。
4.静态内部类单例模式
/**
* 静态内部类单例模式
*/
public class StaticClassSingleton {
/**
* 构造方法私有
*/
private StaticClassSingleton() {
}
/**
* 对外暴露的获取方法
* @return 单例对象
*/
public static StaticClassSingleton getInstance() {
return SingletonHolder.staticClassSingleton;
}
/**
* 静态内部类
*/
private static class SingletonHolder {
private static final StaticClassSingleton staticClassSingleton = new StaticClassSingleton();
}
public void get() {
Log.d("StaticClassSingleton", "这就是静态内部类单例模式");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StaticClassSingleton.getInstance().get();
}
}
当第一次加载StaticClassSingleton 类时并不会初始化staticClassSingleton ,只有第一次调用getInstance方法时才会。这种方式不仅能够保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化。
5.枚举单例模式
/**
* 枚举单例模式
*/
public enum EnumSingleton {
INSTANCE;
public void get() {
Log.d("EnumSingleton", "这就是枚举单例模式");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EnumSingleton.INSTANCE.get();
}
}
枚举单例最大的优点时写法简单,默认枚举实例的创建时线程安全的,并且在任何情况都是一个单例。
五.总结
单例模式时运用频率很高的模式,综合各方面考虑,推荐使用双重检查锁定单例模式,静态内部类单例模式,枚举单例模式。
单例对象如果持有Context,那么很容易引起内存泄漏,需要注意传递给单例对象的Context最好是Application的Context。