吐槽
今天操作系统上课第一天,自己就腰间盘吐出了,被老师记住了,尴尬啊啊啊啊,以后周末还是把这周学的操作系统好好总结下,不然就完了啊啊啊。
参考资料《Android源码设计模式》
什么是单例模式
确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例
这个可能是我之前写项目时候唯一用到的设计模式,写2048游戏的时候就用过这个,不过当时没想这么多。
应用场景
- 确保某个类有且只有一个对象的场景,避免浪费资源
- 某种类型的对象只有一个,者避免多个实例由于多次调用
- 一般访问IO或者数据库等资源的时候考虑
设计思想
因为不允许其他程序new对象然后还有提供一个可以让其他程序获取该对象的方法
所以
- 构造函数不对外开放,一般为Private
- 通过一个静态方法或者枚举返回单列类的对象
- 确保单列的对象有且只有一个,尤其是在多线程的环境中
- 确保单列对象在反序列化的时候不会重新构造对象
写法
饿汉模式
优点:类加载的时候就完成实例化,避免线程同步问题
缺点:由于在类加载的时候就进行了实例化,所以没有达到Lazy Loading(懒加载)
的效果,即使我们没用到这个实例,但是他还是会加载,从而造成内存浪费(可以忽略,
最常用的一种)
public class Student(){
//对象私有,静态
private static Student student = new Student();
//构造方法私有
private Student(){
}
//公有的静态函数,让外面获取这个对象
public static Student getStudent(){
return student;
}
}
从上面的代码我们可以看出来:
- Student类不能以new的形式构造出来,只能通过Student student = Student.getStudent();获取
- 上面的Student对象是静态的,并在声明的时候已经初始化了,确保了Student对象的唯一性
- Student类通过一个静态方法,返回一个静态对象
懒汉模式(线程安全,不建议)
懒汉模式是声明一个静态对象,并在用户的第一次调用静态方法时候进行初始化
优点:单例在实用的时候才会初始化,节约了资源
缺点:第一次加载的时候需要及时的实例化,反应慢,而且每次在调用getStudent都进行同步
public class Student(){
private static Student student;
private Student(){
}
public static synchronized Student getStudent(){
if(student == null){
student = new Student();
}
return student;
}
}
证了线程安全,但是每个线程在想要获得实例时,执行getInstance()
方法都需要进行同步,而实例化代码只需执行一次就够了,后面获取该实例,
直接return即可,方法进行同步效率太低
懒汉模式双重校验锁(DCL)
DCL模式实现单例模式的优点是既能够在需要的时候才初始化单列,又能保证线程的安全,且单列对象初始化后调用getStudent()不进行同步锁
public class Student {
private static Student student = null;
private Student() { }
public static Student getStudent() {
if(Student == null) {
synchronized(Student.class) {
if(Student == null) {
student = new Student();
}
}
}
return student;
}
}
1
代码中进行了两次if检查,这样就可以保证线程安全,初始化一次后,
后面再次访问时,if检查,直接return 实例化对象。
静态内部类单例模式(推荐)
虽然DCL模式下的一定程度解决了资源销毁,多余的同步,线程安全等问题,但是还存在失效的问题。
所以选择静态内部类解决这个问题
public class Student(){
private Student(){
}
public static Student getStudent(){
return StudentnHolder.student1;
}
private static class StudentnHolder{
private static final Student studnet1= new Student();
}
}
当第一次加载的时候Student类的时候并不会初始化student1这个对象,只有在第一次调用Student的getStuedents才会让student1被初始化。
这样就满足了线程安全和保证了单例对象的唯一性
枚举单列(推荐)
public enum StudentEnum {
INSTANCE;
private Student student;
StudentEnum() {
student = new Student()
}
public Student getStudent() {
return student;
}
}
访问方式:StudentEnum.INSTANCE.getStudent;
INSTANCE即为SingletonEnum类型的引用,得到它就可以调用
枚举中的方法。既避免了线程安全问题,还能防止反序列化
重新创建新的对象,但是失去了类的一些特性,没有延时加载,
使用容器实现单例模式
public class SingletonManager {
private static Map<String,Object> objMap = new HashMap<String,Object>();
private Singleton() { }
public static void registerService(String key,Object instance) {
if(!objMap.containsKey(key)) {
objMap.put(key,instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象
这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度
总结
感觉看了这么多,自己写项目时候设计类的话也要好好看下,不管是那种的单例模式,都是将构造方法私有化,然后通过静态的方法获取唯一的单列,但要保证线程安全各种事情。