单例模式已经不陌生了,这次在这个路口再次遇见了。
第一次遇见:单例模式,几乎是见名之意,单例(单个,只有一个,实例)。第一次看设计模式方面的书单纯的是为了理解而理解,现在想想当时真的不应该在那个地方花费太长的时间,因为什么东西都不可能一遍就能会的~~~而且理论和实践有相当长的距离~~~(~ 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;
- }
懒汉式和饿汉式单例模式:
就不介绍懒汉式和饿汉式的由来,都是巨人们创造出来的,很形象很生动,让我们一下子就能知道他们的区别。我们能做的就是站在巨人的肩膀上学习。。学习好了才能进行创造。。。
- <strong> 饿汉式:</strong>
- public class Singleton{
- private static Singleton singleton = new Singleton ();
- private Singleton (){}
- public Singleton getInstance(){return singletion;}
- }
- <strong> 懒汉式:</strong>
- 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++中需要程序员手动释放资源,这个单例就依赖于程序员了,增加了程序员的负担,一旦程序员不小心忘记了。。。会导致内存泄露问题。。。
大概总结到这,以后再遇见再理解啦。。。愿有一天你能融入我的生活中,融入我的血液中,携手到老。