Android 常用单例模式简单介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zp0119/article/details/78950025
   /**
     * 方式一
     * instance 单例初始值是null,还未构建,则构建单例对象并返回;是懒汉模式    - 线程不安全
     * instance 单例对象一开始就被new 出来,主动构建,则不需要判空操作;是饿汉模式     - 线程安全
     */
    private ZpDanLiDemo(){}

    private static ZpDanLiDemo instance = null;

    public static ZpDanLiDemo getInstance() {
        // (场景1:ZpDanLIDemo刚被初始化,线程1、2两个线程同时调用 getInstance 方法,
        // 所以instance为空,两个线程同时通过条件判断,对象创建了两次)
        if (instance == null) {
            instance = new ZpDanLiDemo();
        }
        return instance;
    }

注:这种方式是线程不安全的,具体原因可以看注释描述。

        懒汉与饿汉两种单例模式总是傻傻分不清,应用与面试的时候要多注意。


    /**
     * 方式二
     */
    private ZpDanLiDemo(){}

    // volatile 对象new的时候,JVM执行顺序保证正常执行
    private volatile static ZpDanLiDemo instance = null;

    public static ZpDanLiDemo getInstance() {
        // 双重检测机制
        if (instance == null) {
            // 同步锁 (为了对象不被 new 多次,使用同步锁,锁住整个类)
            synchronized (ZpDanLiDemo.class) {
                // 双重检测机制 (进入synchronized临界区以后,还要再做一次判空。
                // 因为当两个线程同时访问的时候,线程A构建完对象,线程B也已经通过
                // 了最初的判空验证,不做第二次判空的话,线程B还是会再次构建instance对象)
                if (instance == null) {
                    instance = new ZpDanLiDemo();
                }
            }
        }
        return instance;
    }

注:这种方式是线程安全


    /**
     * 方式三 静态内部类实现单例模式
     * 从外部是无法访问静态内部类lazyHolder,只有当调用getInstance方法的时候,才能得到单例对象。
     * instance 对象初始化的时机并不是在单例类ZpDanLiDemo被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。
     * 因此这种实现方式是利用classLoader的加载机制来实现懒加载,并保证构建单例的线程安全。
     */
    private ZpDanLiDemo(){}

    private static class LazyHolder {
        private static final ZpDanLiDemo instance = new ZpDanLiDemo();
    }

    public static ZpDanLiDemo getInstance() {
        return LazyHolder.instance;
    }

注:线程安全

使用静态内部类构建单例,事件比较靠谱的一件事儿。个人喜好,是比较喜欢用这种方式。

使用反射机制打破单例

    /**
     * 利用反射打破单例
     * 使用枚举可以防止反射构建
     */
    private void getDanLi() {
        try {
            // 获得构造器
            Constructor con = ZpDanLiDemo.class.getDeclaredConstructor(ZpDanLiDemo.class);
            // 设置为可访问
            con.setAccessible(true);
            // 构造两个不同的对象
            ZpDanLiDemo zpDanLiDemo1 = new ZpDanLiDemo();
            ZpDanLiDemo zpDanLiDemo2 = new ZpDanLiDemo();
            // 验证是否是不同对象 (log - > false)
            Log.e("zpan", "====" + zpDanLiDemo1.equals(zpDanLiDemo2));

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }


猜你喜欢

转载自blog.csdn.net/zp0119/article/details/78950025
今日推荐