饿汉式
public class Singleton {
private static final Singleton mSingleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return mSingleton;
}
}
第一次声明的时候实例化,后续通过getInstance得到唯一的实例,速度快,但是线程不安全。
懒汉式
public class Singleton {
private static Singleton mSingleton ;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (mSingleton==null){
mSingleton=new Singleton();
}
return mSingleton;
}
}
使用的时候实例化,通过synchronized保证了线程安全,但是每次获取实例都会进行同步,资源消耗大。
Double Check Lock(DCL)
public class Singleton {
private volatile static Singleton mSingleton = null;
private Singleton(){}
public static Singleton getInstance() {
if (mSingleton==null) {
synchronized (Singleton.class) {
if (mSingleton==null) {
mSingleton=new Singleton();
}
}
}
return mSingleton;
}
}
在懒汉式的基础上做了优化,对实例进行了两次null判断,避免了每次获取实例都同步,并且加入了volatile关键字,保证了都是从主内存中获取对象,大部分都是用这种模式。
静态内部类
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.mSingleton;
}
private static class SingletonHolder{
private static final Singleton mSingleton = new Singleton();
}
}
当第一次加载Singleton的时候并不会初始化mSingleton,只有在第一次调用getInstance的时候才会加载SIngletonHolder类。不仅能保证线程安全,也能保证单例的唯一性,也延迟了单例的实例化,比较推荐。
枚举单例
public enum Singleton{
INSTANCE;
public void doThing(){
System.out.println(this.hashCode());
}
}
使用时可以通过Singleton singleton = Singleton.INSTANCE;来获取单例。写法简单,而且默认线程安全,任何情况下都是一个单例。
防止反序列化导致新建实例
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.mSingleton;
}
private static class SingletonHolder{
private static final Singleton mSingleton = new Singleton();
}
private Object readRedolve() throws ObjectStreamException{ //通过添加这个方法实现
return SingletonHolder.mSingleton;
}
}
使用容器实现单例
public class SingletonManager {
private static Map objMap = new HashMap<>();
private SingletonManager(){}
public static void registerService(String key,Object obj){
if (!objMap.containsKey(key)){
objMap.put(key,obj);
}
}
public static Object getService(String key){
return objMap.get(key);
}
}
在程序的开始,将许多要单例的对象放到一个容器里,用的时候根据key取得对应的单例对象。
优点:很多
缺点:1.不能扩展 2.单例模式如果持有context容易引起内存泄露,应该传入Application Context.
package com.dp.example.templatemethod;
/**
* 抽象父类Computer
* @author mrsimple
*
*/
public abstract class AbstractComputer {
protected void powerOn() { //注意不是抽象借口
System.out.println("开启电源");
}
protected void checkHardware() {
System.out.println("硬件检查");
}
protected void loadOS() {
System.out.println("载入操作系统");
}
protected void login() {
System.out.println("小白的电脑无验证,直接进入系统");
}
/**
* 启动电脑方法, 步骤固定为开启电源、系统检查、加载操作系统、用户登录。该方法为final, 防止算法框架被覆写.
*/
public final void startUp() {
System.out.println("------ 开机 START ------");
powerOn();
checkHardware();
loadOS();
login();
System.out.println("------ 开机 END ------");
}
}
package com.dp.example.templatemethod;
/**
* 码农的计算机
*
* @author mrsimple
*/
public class CoderComputer extends AbstractComputer {
@Override
protected void login() { //子类重写
System.out.println("码农只需要进行用户和密码验证就可以了");
}
}
package com.dp.example.templatemethod;
/**
* 军用计算机
*
* @author mrsimple
*/
public class MilitaryComputer extends AbstractComputer {
@Override
protected void checkHardware() {
super.checkHardware();
System.out.println("检查硬件防火墙");
}
@Override
protected void login() {
System.out.println("进行指纹之别等复杂的用户验证");
}
}
//使用
package com.dp.example.templatemethod;
public class Test {
public static void main(String[] args) {
AbstractComputer comp = new CoderComputer(); //新建一个CoderComputer子类对象
comp.startUp(); //执行final算法方法
comp = new MilitaryComputer(); //新建MilitaryComputer子类对象
comp.startUp(); //执行final算法方法
}
优点:1.对流程的封装,封装不变部分,扩展可变部分 2.提取公共部分代码,便于维护
缺点:不利于代码阅读
参考 《Android源码设计模式解析与实战》 — 何红辉 关爱民