定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
结构图
image.png使用场景
-
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
-
单例模式因为Singleton类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。
-
让构造方法private,就堵死了外界利用new创建此类实例的可能。
-
多线程时,要注意同时访问getInstance()方法,导致创建多个实例的可能性。
单例多种写法的例子
- 懒汉模式,同步操作防止多线程创建多个对象。
/**
* 懒汉模式的单例,线程安全
*/
class LazySingleton {
// 私有化构造函数,防止new对象
private LazySingleton() {}
private static LazySingleton instance;
// 同步方式保证线程安全,但是效率比较低下
public static synchronized LazySingleton getInstance() {
if (null == instance) {
instance = new LazySingleton();
}
return instance;
}
}
- 在第一次被引用时,才会将自己实力化,所以就被称为懒汉式单例类。
- 饿汉式单例类Eager Singleton;Android Studio的单例模板就是用这个来做的。
/**
* 饿汉式单例类
*/
class EagerSingleton {
//类加载时就初始化
private static final EagerSingleton ourInstance = new EagerSingleton();
static EagerSingleton getInstance() {
return ourInstance;
}
// 防止new操作
private EagerSingleton() {
}
}
-
这种静态初始化的方式是在自己被加载时就将自己实例化,所以被称之为饿汉式单例类。
-
饿汉式的创建方式在一些场景中将无法使用:譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。
- 双重检验锁
/**
* 双重检验锁方式的单例类
*/
class DoubleCheckSingleton {
// 定义单例静态变量
private volatile static DoubleCheckSingleton instance = null;
// 构造函数私有化
private DoubleCheckSingleton() {}
// 获取单例对象方法
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
-
第1层检验没有synchronized,是异步的,效率较高。第2层检验是synchronized,保证多线程安全
-
实例用volatile修饰。使用 volatile 的主要原因是:禁止指令重排序优化。也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。
- 静态内部类方式
/**
* 静态内部类方式单例
*/
class StaticNestedClassSingleton {
private static class SingletonHolder {
private static final StaticNestedClassSingleton INSTANCE = new StaticNestedClassSingleton();
}
private StaticNestedClassSingleton (){}
public static final StaticNestedClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
-
参考文章: Android单例模式
-
测试界面
- 客户端代码
public class SingletonActivity extends AppCompatActivity {
public static void launch(Context context) {
if (null != context) {
Intent intent = new Intent();
intent.setClass(context, SingletonActivity.class);
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
}
TextView checkTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singleton);
setTitle("单例模式");
checkTextView = findViewById(R.id.textViewCheck);
}
public void onLazyButtonClick(View view) {
LazySingleton lazySingleton1 = LazySingleton.getInstance();
LazySingleton lazySingleton2 = LazySingleton.getInstance();
checkSingleton(lazySingleton1, lazySingleton2);
}
public void onEagerButtonClick(View view) {
EagerSingleton eagerSingleton1 = EagerSingleton.getInstance();
EagerSingleton eagerSingleton2 = EagerSingleton.getInstance();
checkSingleton(eagerSingleton1, eagerSingleton2);
}
public void onDoubleCheckButtonClick(View view) {
DoubleCheckSingleton doubleCheckSingleton1 = DoubleCheckSingleton.getInstance();
DoubleCheckSingleton doubleCheckSingleton2 = DoubleCheckSingleton.getInstance();
checkSingleton(doubleCheckSingleton1, doubleCheckSingleton2);
}
public void onNestedClassButtonClick(View view) {
StaticNestedClassSingleton staticNestedClassSingleton1 = StaticNestedClassSingleton.getInstance();
StaticNestedClassSingleton staticNestedClassSingleton2 = StaticNestedClassSingleton.getInstance();
checkSingleton(staticNestedClassSingleton1, staticNestedClassSingleton2);
}
private void checkSingleton(Object object1, Object object2) {
String message = "";
if (object1 == object2) {
message = "对象object1:" + object1 + "\n"
+ "对象object2:" + object2 + "\n"
+ "是相同的实例";
} else {
message = "对象object1:" + object1 + "\n"
+ "对象object2:" + object2 + "\n"
+ "是不同的实例";
}
checkTextView.setText(message);
}
}
Demo地址
https://gitee.com/zhangxusong888/Android/tree/master/design_pattern
网友评论