一、 单例设计模式

1. 单例设计模式介绍

  • 单例设计模式必须保证只有一个实例存在。例如使用ImageLoader,ImageLoader中包含线程池、网络请求等。非常消耗资源,使用单例模式可以节省系统内存的开销。

2. 单击设计模式使用场景

确保这个类只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该只有一个。

3. 单例设计模式UML类图

单例设计模式

4. 单例设计模式java代码

1.懒汉模式:

  • 代码:
public class Singleton {
                private static Singleton instance;

                private Singleton() {
                }

                public static synchronized Singleton getInstance() {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                    return instance;
                }
            }
  • 优点:
    • 单例只有在使用时才会被实例化,一定程度上节约资源
  • 缺点:
    • 每次调用getInstance都会进行同步,消耗不必要的资源。

2.Double Check Lock(DCL)实现单例

  • 代码:
public class Singleton {
                private static Singleton mInstance = null;

                private Singleton() {
                }

                public void doSomething() {
                    System.out.println("do something");
                }

                public static Singleton getsInstance() {
                    if (mInstance == null) {
                        synchronized (Singleton.class) {
                            if (mInstance == null) {
                                mInstance = new Singleton();
                            }
                        }
                    }
                    return mInstance;
                }
            }
  • 优点:
    • 既能够在需要时初始化,有能够保证线程安全。是使用最多的单例实现方式,一般能够满足大多数需求
  • 缺点:第一次加载时反应稍慢,偶尔会发生失败。

  • 3.静态内部类单例模式

    • 代码:
public class Singleton {
                private Singleton() {
                }

                public static Singleton getInstance() {
                    return SingletonHolder.mInstance;
                }

                /**
                 * 静态内部类
                 */
                private static class SingletonHolder {
                    private static final Singleton mInstance = new Singleton();
                }
            }
  • 优点:
    • 当第一次加载Singleton类时并不会初始化mInstance,只有在第一次调用Singleton的getInstance方法时才会导致mInstance被初始化。因此这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,所以这是推荐使用的单例模式实现方式。

4.枚举单例

  • 代码:
public enum SingletonEnum {
                INSTANCE;

                public void doSomething() {
                    System.out.println("do something");
                }
            }
  • 优点:
    • 默认枚举实例的创建时线程安全的,并且在任何情况下它都是一个单例的 在上述几种单例模式实现中(除了枚举),在一中情况下它们会出现重新创建对象的情况. 就是反序列化。
    • 通过反序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效得获得一个实例,即使构造函数是私有化的,反序列化依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。
    • 反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的、被实例化的方法readResolve(),这个方法可以让开发人员控制对象的反序列化。
    • 以上几个实例中,杜绝对象在反序列化时重新生成对象,必须加入以下方法:
/**
                 * 在readRSovle方法中,将mInstance对象返回,  
                 * 而不是重新生成一个新的对象。
                 * 对于枚举,不会出现这种情况。
                 * @return
                 * @throws ObjectStreamException
                 */
                private Object readResolve() throws ObjectStreamException {
                    return mInstance;
                }

5.使用容器实现单例

  • 代码:
public class SingleManager {
                private static Map<String, Object> objectMap = new HashMap<String, Object>();

                private SingleManager() {
                }

                /**
                 * 添加到容器中
                 *
                 * @param key
                 * @param instance
                 */
                public static void registerService(String key, Object instance) {
                    if (!objectMap.containsKey(key)) {
                        objectMap.put(key, instance);
                    }
                }

                /**
                 * 从容器中取出单例
                 *
                 * @param key
                 * @param instance
                 * @return
                 */
                public static Object getService(String key, Object instance) {
                    return objectMap.get(key);
                }
            }
  • 优点:

    • 可以统一管理多种类型的单例。对用户隐藏了具体实现,降低了耦合度。
  • 6.小结

    • 不管以哪种形式实现单例模式,核心原理都是将构造函数私有化,并通过静态方法获取唯一个实例在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。

5. 单例设计模式在Android系统中

1. Context:

  • 在Android系统中,我们会经常通过Context获取系统级别的服务,如WindowManagerService、ActivityManagerService等。
  • Context 的具体实现类是ContextImpl,在ContextImpl代码中可以看到,在虚拟机第一次加载该类时会注册各种ServiceFatcher,其中就包含LayoutInflater、Service。将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需要根据key来获取到对应的ServiceFetcher,然后通过ServiceFetcher对象的getService函数创建服务对象。

2. LayoutInflater:

  • LayoutInflate在解析时,如果遇到了只写类名的View,那么认为是内置的View控件;如果是自定义控件,那么必须写完整路径。
  • 内置View和自定义View最终都调用了createView方法。createView内部机制是:如果有前缀,那么构造View的完整路径,并将该类加载到虚拟机中,然后获取该类的构造函数并且缓存起,再通过构造函数来创建该View的对象,最后将View返回。

6. 单例设计模式在Android开发中

1. 效果:

ViewPager+Fragment  

android单例设计模式应用

2. 代码:

单例应用包结构

  1. FragmentManager
    1. FragmentManager本身采用了DoubleCheckLock实现单例。
    2. createFragment(int index)获取Fragment。首先FragmentManager会创建一个Map用户存储已经创建的Fragment。如果用户已经创建,直接根据index返回即可。如果没有则创建,并将其存入Fragment中。
public class FragmentsManager {
                    public static final int NEWS = 0;
                    public static final int MUSIC = 1;
                    public static final int BOOK = 2;
                    public static final int ABOUT = 3;
                    public static FragmentsManager mInstance;
                    public static Fragment mFragment;
                    public static final int FRAGMENT_SIZE = 4;
                    public static Map<Integer, Fragment> fragmentMap = new HashMap<Integer, Fragment>();

                    private FragmentsManager() {
                    }

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

                    /**
                     * 采用工厂类进行创建Fragment,保证唯一性
                     *
                     * @param index
                     * @return
                     */
                    public static Fragment createFragment(int index) {
                        mFragment = fragmentMap.get(index);
                        if (mFragment == null) {
                            switch (index) {
                                case NEWS:
                                    mFragment = new NewsFragment();
                                    break;
                                case MUSIC:
                                    mFragment = new MusicFragment();
                                    break;
                                case BOOK:
                                    mFragment = new BookFragment();
                                    break;
                                case ABOUT:
                                    mFragment = new AboutFragment();
                                    break;

                                default:
                                    break;
                            }
                            fragmentMap.put(index, mFragment);
                        }
                        return mFragment;
                    }
                }
    3. MainActivity  
public class MainActivity extends AppCompatActivity {

                private ViewPager vp_content;
                private AboutFragment aboutFragment;
                private BookFragment bookFragment;
                private MusicFragment musicFragment;
                private NewsFragment newsFragment;
                private FragmentsManager fragmentsManager;

                @Override
                protected void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.activity_main);
                    initVariables();
                    initView();
                    initData();
                }


                private void initVariables() {
                    fragmentsManager = FragmentsManager.getInstance();
                }

                private void initView() {
                    vp_content = (ViewPager) findViewById(R.id.vp_content);
                }

                private void initData() {
                    vp_content.setAdapter(new fragmentsAdapter(getSupportFragmentManager()));
                }

                class fragmentsAdapter extends FragmentStatePagerAdapter {

                    public fragmentsAdapter(FragmentManager fm) {
                        super(fm);
                    }

                    @Override
                    public Fragment getItem(int position) {
                        return fragmentsManager.createFragment(position);
                    }

                    @Override
                    public int getCount() {
                        return FragmentsManager.FRAGMENT_SIZE;
                    }
                }

            }
    4. NewsFragment等:
public class AboutFragment extends Fragment {
                @Nullable
                @Override
                public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
                    TextView textView = new TextView(getContext());
                    textView.setText("关于");
                    return textView;
                }
            }

7. 总结

  • 由于在客户端通常并没有高并发的情况,所以选择那种实现方式并不会有太大的影响。处于效率考虑,推荐使用Double Check Lock静态内部类单例设计模式
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值