目录
5. Android Studio 快速创建SingleTon类
1. 单例的定义
保证此类只有一个自己创建的实例对象存在,并且提供全局访问该实例对象的方法。
单例的四大原则:
1. 构造私有。
2. 以静态方法或者枚举返回实例。
3. 确保实例只有一个,尤其是多线程环境。
4. 确保反序列换时不会重新构建对象
2. 使用单例模式的目的
当一个对象被很多地方调用的时候,比如我们的网络请求库, okhttpClient, Retrofit, 我们可以只实例化一次,不用频繁的进行创建和销毁,从而来节省系统资源。
3. 单例的几种创建方式
单例的创建方式有很多中,个人认为主要是看是在单线程中运行还是在多线程中运行。
3.1 单线程中运行单例
在单线程中可以很好的运行,但是在多线程中存在线程安全问题,也称为懒汉式单例。
public class SingleTonVersionOne {
private static SingleTonVersionOne ourInstance;
public static SingleTonVersionOne getInstance() {
if (ourInstance == null) {
ourInstance = new SingleTonVersionOne();
}
return ourInstance;
}
private SingleTonVersionOne() {
}
}
3.2 多线程中的单例
双重检索,在多线程中保持高性能。
public class SingleTonVersionTwo {
/**
* 为了防止出现DCL失效问题,所以用 volatile 关键字来修饰 instance。
*/
private volatile static SingleTonVersionTwo ourInstance;
public static SingleTonVersionTwo getInstance() {
if (ourInstance == null){
synchronized (SingleTonVersionTwo.class) {
if (ourInstance == null) {
ourInstance = new SingleTonVersionTwo();
}
}
}
return ourInstance;
}
private SingleTonVersionTwo() {
}
}
3.2.1 synchronized作用
首先,synchronized是Java中的一个关键字,来解决多线程中的并发同步问题。保证同一时刻最多只有一个线程(被synchronized修饰的代码或方法)执行,其他线程必须等待当前线程执行完后才能执行,从而保证了线程的安全。具体可以看我的另一篇文章 Android程序员重头学Synchronized
3.2.2 DCL失效
上述代码中,可以发现,我们为 ourInstance 加上了 volatile 关键字修饰,说是防止出现 DCL 失效问题。如果你想进一步研究,请看我的另一篇文章 Android Volatile 关键字学习
3.3 静态内部类
确保线程安全,又保证单例的唯一性。
public class SingleTon {
private SingleTon() {
}
private static class SingleTonHelper {
private static SingleTon INSTANCE = new SingleTon();
}
public static SingleTon getInstance() {
return SingleTonHelper.INSTANCE;
}
}
静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化 INSTANCE,故而不占内存。即当 SingleTon 第一次被加载时,并不需要去加载 SingleTonHelper,只有当 getInstance() 方法第一次被调用时,才会去初始化 INSTANCE,且在第一次调用 getInstance() 方法会导致虚拟机加载 SingleTonHelper 类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
4. 测试
新建一个activity来测试我们的SingleTon类
public class SingleTonPatternTestActivity extends AppCompatActivity {
private static final String TAG = "SingleTonPatternTestActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_ton_pattern_test);
SingleTonVersionOne instanceOne = SingleTonVersionOne.getInstance();
SingleTonVersionOne instanceTwo = SingleTonVersionOne.getInstance();
Log.d(TAG, "instanceOne hash code: " + instanceOne.hashCode());
Log.d(TAG, "instanceTwo hash code: " + instanceTwo.hashCode());
instanceOne.setName("IM Kobe");
instanceOne.setName("IM James");
TextView instanceOneTv = findViewById(R.id.instance_one);
instanceOneTv.setText(instanceOne.getName());
TextView instanceTwoTv = findViewById(R.id.instance_two);
instanceTwoTv.setText(instanceTwo.getName());
}
}
log信息为:
D/SingleTonPatternTestActivity: instanceOne hash code: 186887382
D/SingleTonPatternTestActivity: instanceTwo hash code: 186887382
运行结果:
从上方打印出来的Log信息中可以看出,instanceOne和instanceTwo的hashCode是一样的,所有两个对象是一样的,指向同一内存地址。所以,当我们先给instanceOne设置Name为“IM Kobe”,然后接着给instanceTwo设置Name为“IM James”,本质上就是为同一对象设置了两值,且第一个值将被第二个值所覆盖,所以instanceOne和instanceTwo都将显示为“IM James”。如下图所示:
5. Android Studio 快速创建SingleTon类
利用Android Studio来快速创建SingleTon模式的类?
点击OK,新建成功
public class JereSingleTonPatternTest {
private static final JereSingleTonPatternTest ourInstance = new JereSingleTonPatternTest();
public static JereSingleTonPatternTest getInstance() {
return ourInstance;
}
private JereSingleTonPatternTest() {
}
}
一旦 JereSingleTonPatternTest 类被加载时就会去实例化 JereSingleTonPatternTest 对象出来存在内存中,以空间换时间,不存在线程安全问题,称之为饿汉式。
6. 单例模式在项目中运用是好是坏
正方:当很多地方用到一个实例化对象时,我们只需实例化一次,不需要在每次调用时实例化对象,用完然后在销毁,这样能很好的节省系统内存,并且实现起来很简单。很好用。
反方:单例模式就是一个静态的全局变量,你可以在项目中的任意位置调用它,并且修改它,所以有的人就认为不该使用单例模式,当不用单例的时候就需要用依赖注入的方法来解决这个问题。
总结:就我个人而言,我目前的项目已经开发了五六年之久,我可以在项目中看到很多单例,项目的前辈们很喜欢写单例,导致后面加进来的程序员也都跟着很喜欢写单例,包括我自己,单例真的很好用,实现起来也很简单,但我目前还是觉得要少用,毕竟这是个全局变量。很多登入的用户信息都开发成了单例,因为项目中很多地方,可以说百分之八十的类都跟用户信息这个类相关,所以我们开发成了单例。但,万一哪天,需要将app开发成多用户登入时,由于之前你将用户信息写成了单例,你就要重构这用户信息类了,重构影响的范围达到百分之八十。。。
End !欢迎交流指教!