全面分析单例模式

单例的问题:


在项目中,有时我们希望某个类的对象,在整个系统中只有一个实例(如windows里的回收站),这时,我们就需要单例模式。


它就满足几个条件:
1、单例类可以自己控制自己产生对象,这样,它就可以实现对生产的条件控制,数量控制等
2、能对外开放:单例类必须给所有其他对象提供这一实例。


想完成第一个目标,我们只需要把需要的单例的类的构造方法置为private的。
这样除了Car自己,其他的都不能产生car的实例了(无法调构造方法,就无法建立实例).


但是如果仅这样,我们肯定不能完成第二个目标。因为没人能调用到它产生的对象了。
所以我们还要做点修改:


有两种可选的修改:


1.把该类的实例,赋给该类的一个静态成员变量----饿汉式
然后,向外提供一个getInstance方法,返回给调用者这个静态的成员变量。
由于静态变量在data segment中,只有一个。所以,就实现了单例模式。
由于这个实例是在该类的初始化时就生成了,所以有人称之为"饿汉式单例"。
代码:
public class Car {
int a = 1;
///或根据需要限定为final
private static Car car = new Car();
private Car(){
//private constructor
}
//单例模式之饿汉式
/*任何的类,只要在它里面有了控制产生它本身对象的逻辑,基本上就可以把它称之为工厂*/
//有人把getInstance称为静态工厂方法
public static Car getInstance(){
return car; //每次,都返回的都是static成员变量,都是已经产生了的实例
}
public void run(){
System.out.println("Running with smoke");
}
}


验证:
public static void main(String[] args) {
Car c = Car.getInstance();
Car c2 = Car.getInstance();
c.a = c.a + 1;
c2.a = c2.a + 1;
///验证
System.out.println(c==c2);
System.out.println(c.a);
System.out.println(c2.a);
}


2.如果我们不希望在初始化的时候就创建实例,可以采用第二种方法----懒汉式
让一个静态的方法getInstance来控制是否要产生新的实例,这样就存在一个多线程的问题。
所以,还要加上synchronized。
public synchronized static Car getInstance() {
if (car == null) {
car = new Car();
}
return car;
}
完整代码如下:
public class Car {
int a = 1;
private static Car car;
private Car() {
// private constructor
}
//在test.java中执行getInstance时先执行下面这段static,然后再判断
static {
car = new Car();
}
//静态工厂方法 
public synchronized static Car getInstance() {
if (car == null) {
car = new Car();
}
return car;
}
}
测试代码同上面的测试一样:


输出都是:
true
3
3

两种方式的区别和联系:
饿汉式单例类在自己被加载时就将自己实例化。即便加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。
单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。

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

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

/*多例,产生多个对象,可以视情况决定返回哪个private static List<Car> cars = new ArrayList<Car>();*/


附加:

登记式单例类.
import java.util.HashMap;
import java.util.Map;

//登记式单例类,类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
    private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
    static{
        Singleton3 single = new Singleton3();
        map.put(single.getClass().getName(), single);
    }
    
    //保护的默认构造子
    protected Singleton3(){}
    
    //静态工厂方法,返还此类惟一的实例
    public static Singleton3 getInstance(String name) {
        if(name == null) {
            name = Singleton3.class.getName();
            System.out.println("name == null"+"--->name="+name);
        }
        if(map.get(name) == null) {
            try {
                map.put(name, (Singleton3) Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
    
    //一个示意性的商业方法
    public String about() {    
        return "Hello, I am RegSingleton.";    
    }    
    public static void main(String[] args) {
        Singleton3 single3 = Singleton3.getInstance(null);
        System.out.println(single3.about());
    }
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭