《android源码设计模式》读书笔记----单例模式

单例模式应用场景:确保某个类有且只有一个对象,避免产生多个对象消耗过多的资源。例如访问I/O、数据库资源等;

单例模式关键点:

1.构造函数私有化

2.通过静态方法或者枚举返回单例对象

3.确保单例类有且只有一个

4.确保单例类在反序列化时不会重新构建对象

实现方式:

1.饿汉式

public class Singleton {
    private Singleton(){}
    private static final Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

2.懒汉式

public class Singleton {
    private Singleton(){}
    private static Singleton instance ;
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式优点是instance在使用时才会被实例化,缺点是第一次实例化时反应稍慢,每次调用getInstance()时都要进行同步,造成不必要的同步开销;

3.Double check:

public class Singleton {
    private Singleton(){}
    private static Singleton instance;
    public static  Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:在需要时才进行初始化单例,保证线程安全,且单例初始化后调用getInstance时不进行同步锁

缺点:在某些情况下造成双重检查锁定失效问题

4.静态内部类模式

public class Singleton {
    private Singleton(){}
    public static  Singleton getInstance(){
        return  SingletonHolder.instance;
    }
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
}

类级内部类相当于其外部类的static成员,它的对象与外部类对象间不存在依赖关系,相互独立,因此可直接创建。而对象级内部类的实例,是必须绑定在外部对象实例上的。

类级内部类只有在第一次被使用的时候才被会装载。

5.枚举单例:

上面几种情况会在反序列化时重新创建对象;而枚举和普通类一样,不仅能够有字段,也有方法,默认枚举实例的创建是线程安全的,并且在任何情况下都是一个单例;

public enum SingletonEnum {
    INSTANCE;
     public void doSomething() {
         System.out.println("doSth");
     }
}

使用如下:

public class TestMain {
    @Test
    public void main() throws Exception{
        SerializeSingletonEnum();//序列化
        SingletonEnum s = DeserializeSingletonEnum();//反序列

    }

    private SingletonEnum DeserializeSingletonEnum() throws Exception, IOException {
        // TODO Auto-generated method stub
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
            new File("E:/Person.txt")));
            SingletonEnum s = (SingletonEnum) ois.readObject();
        System.out.println(s.hashCode());
        ois.close();
        return s;
    }

    private void SerializeSingletonEnum() throws FileNotFoundException,IOException {
        // TODO Auto-generated method stub
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("E:/Person.txt")));
        SingletonEnum s = SingletonEnum.INSTANCE;
        oo.writeObject(s);
        System.out.println(s.hashCode());
        oo.close();
    }

}

实验结果:

392292416
392292416
两个对象的hashcode是一样的,说明enum单例在反序列化时,不重新创建对象;

如果其他单例模式要想在反序列化时也不重新创建对象,可以增加readResolve方法,返回instance;如下:

    protected Object readResolve() throws ObjectStreamException {
        return instance;
    }

5.使用容器实现单例模式

public class SingletonManager {
    private static Map<String,Object> objMap = new HashMap<String,Object>();
    private SingletonManager() {}
    public static void registerService(String key,Object object) {
        if(!objMap.containsKey(key))
            objMap.put(key, object);
    }
    public static Object getService(String key) {
        return objMap.get(key);
    }

}

总结:不管哪种形式的单例模式,核心是构造方法私有化,通过静态方法获取一个唯一的实例,获取过程中保证线程安全,反序列化时不重新创建对象;

选择哪种模式取决于项目本身,如是否复杂的并发环境,JDK版本是否过低,单例对象的资源消耗等;

android源码中的单例模式

在android系统中,经常通过Context获取系统服务,如WindowManagerService、ActivityManagerService,更常用的是LayoutInflater,这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候通过Context.getSystemService(String name)获取。

以LayoutInflater为例:

LayoutInflater.from(context).inflate(layoutId,null);

public static LayoutInflater from(Context context) {
    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}

context的实现类是ContextImpl,g跟踪源码可以发现虚拟机在加载该类时会注册各种ServiceFetcher,其中包括LayoutInflater Service.将这些服务以键值对的形势存储 在一个HashMap中,用户使用时 只需要跟据key来获取到相关服务。当第一次获取时,会调用ServiceFetcher的CreateService方法创建服务对象,然后缓存到一个列表中,之后直接从缓存中取,避免重复获取对象。此种方式就如使用容器实现单例模式一样;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值