单例模式已经不陌生了,这次在这个路口再次遇见了。
第一次遇见:单例模式,几乎是见名之意,单例(单个,只有一个,实例)。第一次看设计模式方面的书单纯的是为了理解而理解,现在想想当时真的不应该在那个地方花费太长的时间,因为什么东西都不可能一遍就能会的~~~而且理论和实践有相当长的距离~~~(~ o ~)~
官方这样描述:在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于协调系统整体行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了再复杂环境下的配置管理。
第二次遇见:在应用简单工厂改造的抽象工厂+配置文件中,由于每次都要去实例化工厂对象,每个调用的都要去实例化工厂类,会占用大量的内存资源,在抽象工厂类中加上单例模式。这样再调用工厂的时候不用每次都实例化工厂类,用的是懒汉式单例模式。那次的遇见,感受着他的“性格”,单例模式掌控着自己的人生,自己是自己的掌控者。。。
第三次遇见:这个路口再次遇见,我不能放过了。。。不知道何时能牵手一直走。。。结合实际再次深入的理解理解。在java中读取xml配置文件的时候,每次都要调用这个读取的类来读取配置文件,于是,我们就只实例化一次读取配置文件的类就ok了,节省了资源。采用懒汉式单例模式。
//读取配置文件类。/** * 采用单例模式解析sys-config.xml文件。 * 解析sys-config.xml文件。 * * */public class XmlConfigReader { //饿汉式。 //私有的静态的成员变量。 private static XmlConfigReader instance = new XmlConfigReader(); //保存jdbc相关配置信息对象。 private JdbcConfig jdbcconfig = new JdbcConfig(); //私有的构造方法。 private XmlConfigReader() { SAXReader reader = new SAXReader(); //拿到当前线程。 InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("sys-config.xml"); try { Document doc = reader.read(in); //取得xml中的Element。1 、取出驱动器的名字。 Element driverNameElt = (Element)doc.selectObject("/config/db-info/driver-name"); //2 、取出url字符串。 Element urlElt = (Element)doc.selectObject("/config/db-info/url"); //3、取出用户名称和密码。 Element userNameElt = (Element)doc.selectObject("/config/db-info/user-name"); Element passwordElt = (Element)doc.selectObject("/config/db-info/password"); //取得jdbc相关配置信息。 jdbcconfig.setDriverName(driverNameElt.getStringValue()); jdbcconfig.setUrl(urlElt.getStringValue()); jdbcconfig.setUserName(userNameElt.getStringValue()); jdbcconfig.setPassword(passwordElt.getStringValue()); //可以取出标签中的值。取出驱动的名称。 /* String driverName = driverNameElt.getStringValue(); String url = urlElt.getStringValue(); String username = userNameElt.getStringValue(); @SuppressWarnings("unused") String password = passwordElt.getStringValue(); */ } catch (DocumentException e) { // TODO: handle exception e.printStackTrace(); } } //公共的静态的入口方法。 public static XmlConfigReader getInstance() { return instance; } /** * 返回jdbc相关配置。 * @return */ public JdbcConfig getJdbcConfig(){ return jdbcconfig; }
懒汉式和饿汉式单例模式:
就不介绍懒汉式和饿汉式的由来啦,都是巨人们创造出来的,很形象很生动,让我们一下子就能知道他们的区别。我们能做的就是站在巨人的肩膀上学习。。学习好了才能进行创造。。。
饿汉式: public class Singleton{ private static Singleton singleton = new Singleton (); private Singleton (){} public Singleton getInstance(){return singletion;} } 懒汉式: public class Singleton{ private static Singleton singleton = null; public static synchronized synchronized getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
没有写注释哦,观察这两个类的区别,很容易看到,饿汉式是在开始的时候就给这个类实例化一个对象,并供其他所有的对象使用。而懒汉式最初不会创建这个类的对象,而是在有请求的时候开始创建。并为了保证线程的安全问题,我们在 得到实例的方法getInstance() 前面加上了synchronized(同步的)修饰,这就相当于篮子了有很多白馒头,但一次只允许一个人来拿,并且这个人在拿馒头的时候是独占这个篮子的,把这个篮子上锁,直到拿完馒头为止,再让下一位拿馒头。
这也是单例模式灵魂的所在,保证了这个类在一时间内只有一个自己的实例。懒汉式是以空间换取时间的方式来节省资源。
饿汉式在在开始无论是有人用还是没有人用这个类的实例,在开始就new一个对象,因为在开始已经new了一个类的静态实例,所以不需要考虑java的线程同步问题啦。这种方式是以时间换取空间来节省资源空间。
到底使用懒汉式还是饿汉式?
你到底喜欢苹果还是蜜桃,这就取决于自己了,各自有各自的好处。取决于时间和空间上效率的取舍。
每个人都有优点和缺点,单例模式也如此。
优点就不用说了,缺点嘛。。。我们知道设计模式中讲述了程序的五大原则(捎带着复习一下O(∩_∩)O~):1、单一职责原则(一生的专注)2、开放-封闭原则(你自己的事情不需要其他人知道,你的秘密也不需要其他人知道,但你可以通过开朗的性格多交志同道合的朋友)3、依赖倒转原则(我们依赖于老师,老师依赖于学校,学校依赖于社会,低层模块依赖于高层模块)4、里氏替换原则(你父亲要退休了,你可以顶替他的位置。)5、迪米特法则(以前相亲的时候,两家通信是通过媒人来传达的,来表达双方的意思。。双方根本就不见面。。。)6、合成聚合复用原则(继承是强耦合,少生优生幸福一生。。。计算机的世界中是能做叔侄的不做父子。。。)
1)可以看出单例模式不符合开闭原则,因为单例类的子类如果不去改写父类的静态方法,则使用的是和父类同一个实例,他们的联系太紧密了。。。扩展很困难。。。
2)内存泄露问题。。。java中可以自动的释放资源,如果在C++中需要程序员手动释放资源,这个单例就依赖于程序员了,增加了程序员的负担,一旦程序员不小心忘记了。。。会导致内存泄露问题。。。
大概总结到这,以后再遇见再理解啦。。。愿有一天你能融入我的生活中,融入我的血液中,携手到老。
总之,选择设计模式中的模式要权衡利弊,看程序的主打性能是哪方面,就像不同的人有不同的学习方法,性格,不同的世界,不同的人生,不同的活法,但快乐的感受是相同的。。。