一、单例模式概述
单例模式,笔者的理解就是单实例模式。怎么来说呢?打个比方,我们都知道一山不容二虎,一个山头出现了两只老虎那么必然会出现一些争斗,一些伤亡。那么在程序中会出现什么样的“虎”呢?比如:配置文件、工具类、线程池、缓存、日志对象等。如果创建了多个,那么就可能出现占用过多资源,数据读取不一致等等不是期望中的结果。
那么保证程序中上述性质实例有且只有一个呢?这就要依靠单例模式了。
二、单例模式的饿汉式实现
创建一个类Singleton.java,假设这个类的对象实例在程序中有且只能有一个。另外创建Test.java来模拟外部对Singleton类的访问。至此,我们有以下认识:
- 在Test.java的main()方法中必然可以通过调用Singleton类的默认构造方法来生成多个不同的实例
那么我们如何防止这种情况发生呢?一个有效的思路就是:
- 第一步:将Singleton类的构造方法声明为private,则在main()方法中就不能直接调用其构造方法了,也就不能产生任何实例了。
- 第二步:由于我们需要仅一个实例,所以可以在Singleton类里面实例化一个实例成员。虽然在类里面有一个实例成员,但是访问类成员得先实例化对象才能访问,这仿佛就像产生了死循环一般了。
- 第三步:通过把这个实例声明为static就可以通过类名来访问了。
示例代码如下:
1)Singleton.java:
package com.bebdong.Singleton_pattern;
public class Singleton {
private Singleton(){
}
static Singleton instance=new Singleton();
}
2)Test.java:
package com.bebdong.Singleton_pattern;
public class Test {
public static void main(String[] args) {
Singleton s1=Singleton.instance;
Singleton s2=Singleton.instance;
if(s1==s1)
System.out.println("s1和s2是同一实例");
if(s1!=s2)
System.out.println("s1和s2不是同一实例");
}
}
3)运行结果:
但是这样做的话就违背了面向对象编程中的安全原则,为此我们需要将实例做访问控制即声明为private,这样就不能直接通过类名来访问了。从而需要添加一个静态方法来获取这个静态实例。如下:
Singleton.java:
package com.bebdong.Singleton_pattern;
public class Singleton {
private Singleton(){
}
private static Singleton instance=new Singleton();
public static Singleton getInstance(){
return instance;
}
}
当然相应的,外部访问代码也应做修改:通过调用这个公有的静态方法来获取唯一的实例。运行程序,仍然可以知道s1和s2为同一实例。
三、单例模式的懒汉式实现
我们还是三步走:
- 将构造方法私有化,不允许外部直接创建对象。
- 声明类的唯一实例,以private static 修饰。
- 提供一个用户获取实例的方法,用public static 修饰。
注:和饿汉模式的区别就在于实例化对象的位置,具体参见代码。
Singleton.java:
package com.bebdong.Singleton_pattern;
public class Singleton {
private Singleton(){
}
private static Singleton instance; //只声明,不实例化
public static Singleton getInstance(){
if(instance==null){ //用一个判断条件确定需不需要实例化
instance=new Singleton();
}
return instance;
}
}
Test.java做相应修改,运行可以发现仍然为同一个实例。
四、饿汉模式和懒汉模式的区别
根据上述饿汉式和懒汉式在实现上的细微差别,可以得出如下结论:
- 由于饿汉式实现在声明唯一对象的时候已经初始化,故而当类加载的时候将同时实例化这个唯一的对象。具有类加载慢,获取对象快的特点。
- 而懒汉模式在类加载的时候不会同时实例化这个唯一的对象,它只有当外部第一次时才实例化。具有类加载快,运行时获取对象的速度比较慢的特点。
- 第三个区别可能隐藏得比较深:饿汉模式是线程安全的,而懒汉模式不是线程安全的。