单例模式

来源:互联网 发布:投诉淘宝小二判决电话 编辑:程序博客网 时间:2024/05/18 03:08

华为面试题(单例模式):
java模式之单例模式:
          单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例。
 特点:
        1,一个类只能有一个实例
        2,自己创建这个实例
        3,整个系统都要使用这个实例

       Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。一些资源管理器常常设计成单例模式。
       外部资源:譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干个通信端口,系统应当集中管理这些通信端口,以避免一个通信端口被两个请求同时调用。内部资源,譬如,大多数的软件都有一个(甚至多个)属性文件存放系统配置。这样的系统应当由一个对象来管理这些属性文件。

       还有, singleton能够被状态化; 这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。

另外方面,Singleton也能够被无状态化。提供工具性质的功能,

     Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。

    我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。 

    一个例子:Windows 回收站。
   在整个视窗系统中,回收站只能有一个实例,整个系统都使用这个惟一的实例,而且回收站自行提供自己的实例。因此,回收站是单例模式的应用。

两种形式:
1,饿汉式单例类

public class Singleton {  private Singleton(){}  //在自己内部定义自己一个实例,是不是很奇怪?  //注意这是private 只供内部调用  private static Singleton instance = new Singleton();  //这里提供了一个供外部访问本class的静态方法,可以直接访问    public static Singleton getInstance() {    return instance;      } } 

2,懒汉式单例类

public class Singleton {   private static Singleton instance = null;  public static synchronized Singleton getInstance() {  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次  //使用时生成实例,提高了效率!  if (instance==null)    instance=new Singleton();  return instance;   } } 


(第一种加载类时,instance马上就被实例化,第二中形式是lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了,instance并没有马上被实例化,只有调用方法getInstance()的才被实例化)

要理解模式还是要对java 基本知识同步,static 理解清楚。
看下一个例子就知道了。
jex1:public class Singleton    {        private static final Singleton singleton = null;           private Singleton()        {        }        public static Singleton getInstance()        {            if (singleton== null)            {                singleton= new Singleton();            }            return singleton;        }    }   
这个不多说了,肯定是错误的,如果多个线程访问的时候都==null,那么接下来就是产生多个实例。不算单例模式。
ex2:public class Singleton    {        private static final Singleton singleton = null;           private Singleton()        {        }        public static Singleton getInstance()        {            if (singleton== null)            {                synchronized (Singleton.class) {                    singleton= new Singleton();                }            }            return singleton;        }    } 
这个虽然加了同步锁,但是如果多个线程同步访问==null,那么还是会产生多个实例,只是产生实例的时候同步而已。
ex3: public class Singleton    {        private static final Singleton singleton = null;           private Singleton()        {        }        public static Singleton getInstance()        {            synchronized (Singleton.class)            {                if (singleton== null)                {                    singleton= new Singleton();                }            }            return singleton;        }    }  
这个就可以了,但是这个地方对于每个访问的这个方法都是同步,而最需要的同步只是在new的地方,所以这样做虽然保证了单例,但是效率有点低哦。

ex4:public class Singleton    {        private static final Singleton singleton = null;           private Singleton()        {        }        public static Singleton getInstance()        {            if (singleton== null)            {                synchronized (Singleton.class)                {                    if (singleton== null)                    {                        singleton= new Singleton();                    }                }            }            return singleton;        }    }  
这个不错,保证了单例。这个叫做double-check 双重检查。
还有一个比较简单的写法。
ex5:public class Singleton    {        private static final Singleton singleton = new Singleton();       private Singleton()        {        }        public static Singleton getInstance()        {            return singleton;        }    }  
这个看起来要简单好多。

第三种:
如读 property 属性文件,内存只要一份就可以了。设计为单例模式。在静态代码块。

class PropertiesMgr{private static Properties props=new Properties();static{try {props.load(Test.class.getResourceAsStream("*.propeties"));} catch (IOException e) {e.printStackTrace();}}public String getProperty(String key){return props.getProperty(key);}}
全程就读一次硬盘。
这个有缺陷:改了不能马上起作用,内存缓存没变。 解决办法,文件改了把文件重新load 一遍。


原创粉丝点击