单例模式是我们在项目开发中用得最多的一种设计模式。单例模式要求:1、单例模式的类在整个应用中只有一个实例对象。2、单例模式的类有一个对外方法提供类的实例。为此我们设计单例模式需要满足以下几点:
1、构造函数私有化:使用private私有化构造函数,使外部程序不能创建对象。
2、通过静态方法或者枚举返回一个单例对象。
3、确保单例类的对象有且只有一个,特别是在多线程模式下。
4、确保单例类对象在反序列化时不会重新创建对象
Android中实现单例主要有以下几种方式:
饿汉式:
public class SingleInstance {
//静态final变量 类加载的时候就初始化
private static final SingleInstance INSTANCE = new SingleInstance();
//构造函数私有化
private SingleInstance() {
}
//对外提供实例的静态方法
public static SingleInstance getInstance() {
return INSTANCE;
}
}
饿汉式单例特点:类加载的时候就初始化实例对象,使用效率底。
懒汉式:
public class SingleInstance {
private static SingleInstance instance;
//构造函数私有化
private SingleInstance() {
}
//对外提供实例的静态方法
public static synchronized SingleInstance getInstance() {
if (instance == null) {
instance = new SingleInstance();
}
return instance;
}
}
懒汉式单例特点:
1、在第一次使用单例调用getInstance()方法时才会初始化。
2、使用synchronized 实现同步,保证单例的唯一性。
3、每次调用getInstance()使用单例时都会去进行同步,浪费资源。
Double Check Lock 双重检查锁定模式(DCL):
DCL模式是平常使用最多的模式,在绝大多数情况下都能满足我们的开发需求,保证单例的唯一性。
public class SingleInstance {
//用volatile修饰变量
private volatile static SingleInstance instance = null;
//构造函数私有化
private SingleInstance() {
}
//对外提供实例的静态方法
public static SingleInstance getInstance() {
if (instance == null) {
synchronized (SingleInstance.class) {
if (instance == null) {
instance = new SingleInstance();
}
}
}
return instance;
}
}
DCL单例模式特点:
1、volatile (JDK1.5后提供的属性)修饰instance对象,用来保证instance对象每次都是从内存中读取。
2、在第一次使用单例调用getInstance()方法时才会初始化。
3、双重null判断:第一次null判断是否需要进行同步,第二次null判断是否需要创建对象。第一次调用getInstance()之后每次使用单例都不会再去同步,节约了资源。
4、由于Java内存模型的原因或者高并发环境下会偶尔锁定失败。
5、JDK1.5.以及以下版本不能保证对象的唯一性。
静态内部类单例模式:
public class SingleInstance {
//构造函数私有化
private SingleInstance() {
}
//静态内部类
private static class SingleHolder {
private static final SingleInstance INSTANCE = new SingleInstance();
}
//对外提供实例的静态方法
public static SingleInstance getInstance() {
return SingleHolder.INSTANCE;
}
}
静态内部类单例模式特点:
1、效率高,在第一次调用getInstance() 方法使用单例时才会进行初始化。
2、静态final对象,保证对象的唯一性。
枚举单例模式:
单元素的枚举类型已经成为实现单例的最佳方法,枚举单例模式是《effective java》中推荐的最好的实现单例的模式。
public enum SingleInstance {//枚举类
INSTANCE;//单例对象
public void fun() {
Log.i("TAG", "枚举单例中的方法");
}
}
//使用枚举单例
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
//枚举单例使用
SingleInstance.INSTANCE.fun();
}
枚举单例特点:
1、保证对象的唯一性。
2.、反序列化的情况下也能保证对象的唯一性(其他单例模式不能在反序列化的情况下保证对象的唯一性)。
单例对象的生命周期和整个应用的生命周期是一样的,所以不管用哪种方式使用单例模式时,我们都需要注意内存的泄露,特别是在使用Context的时候,如果单例对象持有某个Context,比如某个activity的Context,那么就算这个activity退出了内存也得不到释放,会造成内存泄漏,这时最好的方式就是使用Application Context。