Java模式设计之单例模式

来源:互联网 发布:caffe rfcn 编辑:程序博客网 时间:2024/05/22 11:49

        在Java程序设计中经常会用到单例模式,但是很多时候程序员却不知道什么时候该使用单例模式,或者怎么使用单例模式。

  我们总结分析单例模式的时候,了解到单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个事例;三是它必须自行向整个系统提供这个实例。在下面的对象图中,有一个"单例对象",而"客户甲"、"客户乙"和"客户丙"是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。

  单例模式在资源管理器中应用。
  一些资源管理器常常设计成单例模式。
  在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
  需要管理的资源包括软件内部资源,譬如,大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置。这样的系统应当由一个对象来管理一个属性文件。
  需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件,记录软件系统内部事件、出错信息的部件,或是对系统的表现进行检查的部件等。这些部件都必须集中管理,不可政出多头。
  这些资源管理器构件必须只有一个实例,这是其一;它们必须自行初始化,这是其二;允许整个系统访问自己这是其三。因此,它们都满足单例模式的条件,是单例模式的应用。

分析单例模式的结构。

单例模式有以下的特点:

1、单例类只可有一个实例。

2、单例类必须自己创建自己这惟一的实例。

3、单例类必须给所有其他对象提供这一实例。虽然单例模式中的单例类被限定只能有一个实例,但是单例模式和单例类可以很容易被推广到任意且有限多个实例的情况,这时候称它为多例模式(Multiton Pattern) 和多例类(Multiton Class)。

  由于Java 语言的特点,使得单例模式在Java 语言的实现上有自己的特点。这些特点主要表现在单例类如何将自己实例化上。
  饿汉式单例类

这是在Java 语言里实现得最为简便的单例类,下面所示的类图描述了一个饿汉式单例类的典型实现。

从图中可以看出,此类已经自已将自己实例化。

  代码清单1:饿汉式单例类

public class EagerSingleton
{
    private static final EagerSingleton m_instance =
    new EagerSingleton();
    /**
     * 私有的默认构造子
    */
    private EagerSingleton() { }
    /**
    * 静态工厂方法
    */
    public static EagerSingleton getInstance()
    {
      return m_instance;
     }
}

 

读者可以看出,在这个类被加载时,静态变量m_instance 会被初始化,此时类的私有构造子会被调用。这时候,单例类的惟一实例就被创建出来了。
  Java 语言中单例类的一个最重要的特点是类的构造子是私有的,从而避免外界利用构造子直接创建出任意多的实例。值得指出的是,由于构造子是私有的,因此,此类不能被继承。

懒汉式单例类
  与饿汉式单例类相同之处是,类的构造子是私有的。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。如果加载器是静态的,那么在懒汉式单例类被加载时不会将自己实例化。如下图所示,类图中给出了一个典型的饿汉式单例类实现。

  代码清单2:懒汉式单例类

package com.javapatterns.singleton.demos;
public class LazySingleton
{
private static LazySingleton
m_instance = null;
/**
* 私有的默认构造子,保证外界无法直接实例化
*/
private LazySingleton() { }
/**
* 静态工厂方法,返还此类的惟一实例
*/
synchronized public static LazySingleton
getInstance()
{
if (m_instance == null)
{
m_instance = new LazySingleton();
}
return m_instance;
}  }

同样,由于构造子是私有的,因此,此类不能被继承。饿汉式单例类在自己被加载时就将自己实例化。即便加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。

  从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时, 必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费时间。这意味着出现多线程同时首次引用此类的机率变得较大。

  饿汉式单例类可以在Java 语言内实现, 但不易在C++ 内实现,因为静态初始化在C++ 里没有固定的顺序,因而静态的m_instance 变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么GoF 在提出单例类的概念时,举的例子是懒汉式的。他们的书影响之大,以致Java 语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合Java 语言本身的特点。
  使用单例模式必要条件:

下面我们探讨一下使用单例模式的必要条件。
  在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。但是有经验的读者可能会看到很多不当地使用单例模式的例子,可见做到上面这一点并不容易,下面就是一些这样的情况。
  例子一
  问:我的一个系统需要一些"全程"变量。学习了单例模式后,我发现可以使用一个单例类盛放所有的"全程"变量。请问这样做对吗?
  答:这样做是违背单例模式的用意的。单例模式只应当在有真正的"单一实例"的需求时才可使用。
  一个设计得当的系统不应当有所谓的"全程"变量,这些变量应当放到它们所描述的实体所对应的类中去。将这些变量从它们所描述的实体类中抽出来,放到一个不相干的单例类中去,会使得这些变量产生错误的依赖关系和耦合关系。
  例子二
  问:我的一个系统需要管理与数据库的连接。学习了单例模式后,我发现可以使用一个单例类包装一个Connection对象,并在finalize()方法中关闭这个Connection对象。这样的话,在这个单例类的实例没有被人引用时,这个finalize() 对象就会被调用,因此,Connection 对象就会被释放。这多妙啊。
  答:这样做是不恰当的。除非有单一实例的需求,不然不要使用单例模式。在这里Connection 对象可以同时有几个实例共存,不需要是单一实例。
  单例模式有很多的错误使用案例都与此例子相似,它们都是试图使用单例模式管理共享资源的生命周期,这是不恰当的。

下面简单说一下,笔者现在学习安卓,写了一个很小的音乐播放播放器,自己写了一个application。Application和Actovotu,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)

package com.frewen.ttplayer;

importjava.util.ArrayList;

importjava.util.LinkedList;

importjava.util.List;

 

importandroid.app.Activity;

importandroid.app.Application;

importandroid.content.Context;

importandroid.graphics.Bitmap;

importandroid.graphics.BitmapFactory;

importandroid.media.MediaPlayer;

importandroid.util.Log;

 

import com.frewen.ttplayer.entry.Music;

import com.frewen.ttplayer.entry.impl.Musicdata;

import com.frewen.ttplayer.util.MusicPreference;

 

public classMyApplication extends Application {

    public static MediaPlayer mediaPlayer;

    public static MusicPreferencemusicPreference;

    public static ArrayList<Music> musics= new ArrayList<Music>();

    public static boolean isStart = false;

    public List<Activity> activityList =new LinkedList<Activity>();

    public static Bitmap bitmap_l;

    public static Bitmap bitmap_s;

    public static MyApplication instance;

 

    public ArrayList<Music> getMusics() {

       return musics;

    }

    public static Context context;

    @Override

    public void onCreate() {

       super.onCreate();

       context = getApplicationContext();

       new Thread(new Runnable() {

           @Override

           public void run() {

              setMusics(Musicdata.getMultiDatas(context));

              bitmap_l = BitmapFactory.decodeResource(context.getResources(),

                     R.drawable.default_bg_l);

              bitmap_s =BitmapFactory.decodeResource(context.getResources(),

                     R.drawable.default_bg_s);

           }

       }).start();

       mediaPlayer = new MediaPlayer();

       musicPreference = new MusicPreference(context);

    }

    public void setMusics(ArrayList<Music>ms) {

       musics.clear();

       musics = ms;

       Log.i("test", "列表长度" + this.musics.size());

    }

 

    public MyApplication() {

    }

    // 单例模式中获取唯一的MyApplication实例

    public static MyApplication getInstance() {

       if (null == instance) {

           instance = new MyApplication();

       }

       return instance;

    }

    // 添加Activity到容器中

    public void addActivity(Activity activity) {

       activityList.add(activity);

    }

    // 遍历所有Activity并finish

    public void exit() {

       for (Activity activity : activityList) {

           activity.finish();

       }

       System.exit(0);

    }

    /**

     * 向musics集合中追加一组miusic信息

     *

     *@param musics

     */

    public void append(ArrayList<Music>musics) {

       if (musics != null) {

           this.musics.addAll(musics);

       }

    }

    public void append(Music music) {

       if (music != null) {

           this.musics.add(music);

       }

    }

}

android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享等,数据缓存等操作。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝付了钱如果商家没货了怎么办 别人拿走我的货不给钱怎么办 快递出途中损坏签单后才发现怎么办 顾客支付宝少付了钱人走了怎么办 顾客通过收钱码付的钱少了怎么办 寄顺丰快递收件人号码填错了怎么办 京东退货售后说是有划痕怎么办 手机上快递员软件签收错了怎么办 苹果手机进网许可证丢了怎么办 作废的普票发票联丢失怎么办 想退换货但是发票丢了怎么办 快递没有当面验货造成的丢件怎么办 退货卖家收货验货说有问题怎么办 没当着快递人员面验货少货怎么办 微信绑定的银行了被快捷支付怎么办 银行支付到微信支付里的钱怎么办 微信绑定银行忘支付密码忘了怎么办 微信桂林银行不可以支付怎么办 微信忘记支付密码和银行卡号怎么办 京东商城迟迟不通过退货审核怎么办 网上购物想退款商家要我拒收怎么办 运输公司对车主不买保险怎么办 苹果平板限制访问密码忘了怎么办 安卓手机忘了解锁密码怎么办 海之蓝扫码显示域名不对这是怎么办 华为手机丢了帐号密码忘记了怎么办 电脑会员系统账号密码忘记了怎么办 推广渠道客户要求免费铺货怎么办 代理机构做的网站找不到人了怎么办 瓷砖买的和描述的不一样怎么办 网上买东西地址的省份写错了怎么办 驾校把预留号码注册错了怎么办 太害羞了不敢进店买东西怎么办 收件箱邮箱邮件满了但不想删怎么办 模拟人生畅玩版任务时间过了怎么办 快递放柜子里长时间不拿怎么办 古筝的筝码序号错了怎么办? 极限竞速3地平线爆内存怎么办 车载导航安卓系统禁止装软件怎么办 在微信上买东西买到假货怎么办 想从新西兰寄红酒到家运费怎么办