您的位置:首页 > 其它

设计模式-单例模式

2016-03-12 20:53 337 查看

1.单例模式介绍

单例模式是应用最广的模式之一。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多系统只需要拥有一个全局的对象,遮掩更有利于我们协调整体的统一行为。

2.单例模式的定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

3.单例模式的使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对只应该有且只有一个。例如,创建 一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式

4.单例模式的分类

饿汉模式和懒汉模式

5.单例模式的UML类图



6.实现单例模式的关键点

1.构造函数对外不开放,一般为private
2.通过一个静态方法或者枚举返回单例对象
3.确保单例类的对象有且只有一个,尤其是在多线程情况下
4.确保单例对象在反序列化时不会被重新构建

7.单例模式的几种写法

7.1 饿汉模式

饿汉模式是类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的

// 普通人群
public class Person {
String name;
int age;

public Person() {
}

public void work() {

}
}

// 教师
public class Teacher extends Person {
public void work() {

}
}

// 校长
public class Principal extends Person {
private static final Principal mPrincipal = new Principal();

// 构造函数私有
private Principal() {

}

// 公有的静态函数,对外暴露获取单例对象的接口
public static Principal getInstance() {
return mPrincipal;
}

}

public class SingletonDemo {
public static void main(String[] args) {
// 教师对象通过New获取
Teacher tea_0 = new Teacher();
Teacher tea_1 = new Teacher();

// 校长对象通过getInstance方法获取
Principal pri_0 = Principal.getInstance();
Principal pri_1 = Principal.getInstance();

// 普通人群对象通过New获取
Person per_0 = new Person();
Person per_1 = new Person();

System.out.printf("tea_0: " + tea_0.toString() + "\n");
System.out.printf("tea_1: " + tea_1.toString() + "\n");
System.out.printf("pri_0: " + pri_0.toString() + "\n");
System.out.printf("pri_1: " + pri_1.toString() + "\n");
System.out.printf("per_0: " + per_0.toString() + "\n");
System.out.printf("per_1: " + per_1.toString() + "\n");
}

}


7.2 懒汉模式

懒汉模式是声明一个静态对象,并且用户在第一次调用getInstance时进行初始化。
优点:单例只有在使用时才会被实例化,在一定程度上节省了资源
缺点:第一次加载时需要及时进行实例化,反应稍慢,最大的问题在于每次调用getInstance都进行同步,造成不必要的同步开销

 
Public class Principal extends Person {
/** 懒汉模式 */
private static Principal mPrincipal = null;

// 构造函数私有
private Principal() {

}

public static Principal getInstance() {
if (null == mPrincipal) {
mPrincipal = new Principal();
}

return mPrincipal;
}
}

7.3 DCL(Double Check Lock)实现单例

在getInstance方法中对mPrincipal进行两次判断,第一次判断主要是为了避免不必要的同步,第二次判断主要是为了在null的情况下创建实例并保证线程安全
优点:既能够在需要时才进行实例初始化,同时保证了线程安全,且单例对象实例化后,调用getInstance不进行同步锁
缺点:第一次加载时反应速度慢,由于JVM内存模型,偶尔会出现实例化失败

public class Principal extends Person {
/** DCL模式 */
private static Principal mPrincipal = null;

// 构造函数私有
private Principal() {

}

public static Principal getInstance() {
if (null == mPrincipal) {
synchronized (Principal.class) {
if (null == mPrincipal) {
mPrincipal = new Principal();
}
}
}
return mPrincipal;
}
}


7.4 静态内部类单例模式

当第一次加载Principal时并不会初始化mPrincipal,只有在第一次调用Principal的getInstance才会实例化mPrincipal。因此在第一次调用getIntance方法时加载PrincipalHolder,这种方式不仅能够保证线程安全也能够保证实例化对象的唯一性。
public class Principal extends Person {
/** 静态内部类实现单例模式 */
// 构造函数私有
private Principal() {

}

public static Principal getInstance() {
return PrincipalHolder.mPrincipal;
}

private static class PrincipalHolder {
private static Principal mPrincipal = new Principal();
}
}

7.5枚举单例

默认枚举创建单例是线程安全的,并且在任何情况下,它都是一个单例。
public enum Principal {

INSTANCE;

private Principal(){

}

public void work(){
}

}
使用
Principal.INSTANCE.work();

7.6 登记模式

在程序初始化时,将多个单例类型注入到一个统一的管理类中,在使用时,根据key获取对应类型的对象,这种方式除了方便我们管理多种类型的单例,

并且在使用时通过统一的接口进行获取操作,实现了对用户的隐藏。
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();

private SingletonManager() {

}

public static void registerSingleton(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}

public static Object getSingleton(String key) {
return objMap.get(key);
}
}</span>


参考资料:

1.Android源码设计模式时间

2.JAVA设计模式之单例模式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: