单例模式

来源:互联网 发布:董洁出轨 知乎 编辑:程序博客网 时间:2024/06/14 09:03

因为本学期开《设计模式》课,正值半学期之际,我趁写博客之时,回忆一下自己这段时间学的的模式。

一.单例模式

1.来源

为什么会提出单例模式这个模式呢?我们在日常使用电脑时,都会使用到“任务管理器”这么一个功能,但是我们却会发现,怎么都不能打开第二个“任务管理器”窗口,这就是单例模式使用后可以出现的效果。

2.针对上面所说的那类情况,即提出单例模式:即一个类有且仅有一个实例,只允许自己实例化,并且对外开放。这里我们要记住,有且仅有一个实例,只允许自己实例化,并且对外开放这句话,因为这是单例模式的核心。

3.单例模式的实际应用

就例如在一个软件系统中,我们需要使用到“任务管理器”这个功能,那么我们该怎么做呢?与其口头叙述,不如直接上代码。如下:

(1).Singleton类

package liu.shen.util;/** * 单例类 * @author Object * */class Singleton{        private static Singleton singleton = null;//新建Singleton引用    /*1.这里为什么只有一个singleton = null,这是一个空的引用,没有管理任何实际的对象值     * 而singleton降修饰private,是因为不想让Singleton之外的类使用。     */        private Singleton(){    /*1.这是Singleton的默认构造函数,但为什么这里将构造函数设置成private呢?     * 答:因为我们一般在生成对象的时候,都有Class c = new Class();这样的语法,但是如果我们使用了     * private来修饰这个构造器,那么就不会产生这种问题了。    */    }        public static Singleton getInstance(){        /*1.getInstance()方法是获取对象实例的方法         * 2.if(singleton != null)具体的作用是什么呢?         * 答:我在第一次看到这么书写的时候,我就在想,为什么这里还要多此一举呢?后来才会明白。请各位读者想一想,         * 假设系统不是第一次调用这个方法,那么是不是代表着singleton这个对象已经存在了?所以我们还有if()一下的需要         * 3.为什么有static修饰呢?         * 答:因为static关键字修饰的方法是表示该方法可以被直接调用,这里的直接调用的意思就是:我们可以直接使用类名.方法名         * 即可对方法进行调用,例如在这里面就是Singleton.getInstance();         */        if(singleton != null){            singleton = new Singleton();        }        return singleton;//返回这个对象    }    }

(2)Client类

package liu.shen.util;/** * 主程序类 * @author Object * */public class Client {public static void main(String [] args){//Singleton singleton = new Singleton();private修饰,不能创建Singleton singleton1,singleton2,singleton3;//创建三个引用//分别将三个对象实例化singleton1 = Singleton.getInstance();singleton2 = Singleton.getInstance();singleton3 = Singleton.getInstance();if(singleton1 == singleton2 && singleton1==singleton2 &&singleton2 == singleton3){System.out.println("都为同一对象!");}else{System.out.println("三个对象不完全相同!");}}}
测试结果如下:


从运行结果上,我们可以看到,singleton1,singleton2,singleton3都是相同的对象。

4.小结
可能经常使用Java从事线程级编程的伙伴就看出了,假设这个
        if(singleton != null){            singleton = new Singleton();        }
代码过程中,有两个同一的线程都在操作,该怎么办呢?或许有人想到了同步的问题。对,就是synchronize,我们使用synchronize来对线程进行锁定。以处理多线程同时访问的问题。
修改后的代码如下:

synchronized public static Singleton getInstance(){     if(singleton != null){singleton = new Singleton();     }     return singleton;//返回这个对象}
这里对线程进行了锁定,但是这么做并不高明,因为这是对整个程序都进行了锁定,这么做会将系统的性能减小很多。
所以又有了另一种方案:
public static Singleton getInstance(){ if(singleton != null){ synchronized(Singleton.class){ singleton = new Singleton(); }}return singleton;//返回这个对象}
注意这里的
synchronized (Singleton.class)
{
   singleton = new Singleton();
}语句,笔者是在写这篇博客的时候才发现原来是这么回事情!我得回去瞅瞅synchronize具体的用法了。
好了,到这儿基本可以了,但是或许还是有人想问,这样还有问题啊!!!
是的,不过我们可以这么修改:
public static Singleton getInstance(){ if(singleton != null){ synchronized(Singleton.class){ if(singleton != null) singleton = new Singleton(); }}return singleton;//返回这个对象}
我们在synchronized{}中还使用了一个if(singleton != null),为什么会在这么做呢?假设出现了下列这么一个情况:
程序A和程序B都运行到了synchronized(Singleton.class)时,A先进入循环,B被锁住,但是当A已经完全创建好singleton对象并准备返回的时候,因为第一个if(singleton !=null)是true,就会导致程序B继续执行下面的代码,但是程序B并不知道程序A已经创建好这个singleton对象了。所以就会出现程序异常。但是在上面的这个程序中,并不会出现这么一个情况。
5.总结
当然,世界上会有其它的方法来实现单例模式,我们来看看下面这个例子
<pre name="code" class="java">class Singleton {private static final Singleton singlton = new Singleton();//直接对其进行新建对象private Singleton(){};public static Singleton getInstance(){return singleton;//返回这个对象}}
这里面直接实例化Singleton,不给别的程序任何主动地机会,但是这个会在程序加载的时候就加载这个对象值,将会导致程序内存空间的浪费。6.说明单例模式是设计模式中最简单的一个模式,因为里面的调用关系很简单,只有一个单例类Singleton,聚合了自己的一个的一个对象,并且整个系统中,仅此一个。我们或许看到能有一个单例模式真的是很棒啊,但是上课时老师说到一个问题就是:我们可以用synchronized来修改锁定线程,可以使用其它方式来创建一个单例,但是假若有别人攻击你的系统,还是会有办法的,那就是Java反射技术。那么什么是Java反射技术呢?所谓Java反射技术,就是指在程序运行时,获取已知名称类,或已有对象的相关信息的一种机制。下面我们使用这种方式来试一下。
try { Constructor con = Singleton.class.getDeclaredConstructor();           con.setAccessible(true);                    // 通过反射获取实例           Singleton single1 = (Singleton)con.newInstance();           Singleton single2 = (Singleton)con.newInstance(); if(singleton1 == single1){System.out.println("通过java 反射技术生成的对象和 单例模式生成的对象相同!");}else{System.out.println("通过java 反射技术生成的对象和 单例模式生成的对象不同!");} if(single2 == single1){System.out.println("通过java 反射技术生成的对象相同!");}else{System.out.println("通过java 反射技术生成的对象不同!");}  } catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}

可以看到,单例模式生成的对象都是同一对象,但是使用反射机制生成的就不是同一个对象了。

或许有人尝试着这么写反射,如下:
Class c = Class.forName("liu.shen.util.Singleton");object = c.newInstance();//生成一个对象  if(singleton1 == object){ System.out.println("singleton 1 = object,即单例模式和反射机制生成的对象相同!");  } else { System.out.println("singleton 1 != object,即单例模式和反射机制生成的对象不同!"); } 

这是最简单的反射模式书写,注意下面几点:
(1).获取类名的时候,如果自己在创建Java Project的时候,不是默认包,那么需要将自己的包名带上,这里就是liu.shen.util;
(2)但即使是这样,我们依然会看到程序抛出“java.lang.IllegalAccessException: ”异常,为什么呢?我们能看到提示说是:Class liu.shen.util.Client can not access a member of class liu.shen.util.Singleton with modifiers "private",即类Client没有权限进入Singleton (的构造方法),因为(构造方法)被修饰为private,我们需要修改Singleton的权限为public才可以得到正确结果,所以本文使用了另一种方式。
7.最后
设计模式纷繁复杂,但是只要记住一点,不要为了使用模式而使用模式,便是最好的模式。
最后,注明,本博客书写部分来源于《设计模式的艺术之道》一书<刘伟著>,加之本人水平有限,读者且带着批判的眼光去读,错误之处,还请指出。


1 0
原创粉丝点击