问题的提出:在一个系统运行期间,某个类只需要一个实例。
单例模式的本质:控制实例个数。
用来解决这个问题的一个合理方案就是单例模式(singleton)。单例模式是指:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
解决方案:首先,一个类之所以可以创建多个实例,主要原因是它的构造方法是公开的,这样只要包含了这个类所在的包,程序就能肆意生成实例。所以首先要收回实例生成的权限,将构造函数设置为private。其次,在运行期间只能有一个实例,我们很自然的就想到了static的限制。
在具体实现中,我们有以下两种设计方法:
1) 懒汉式
// a Lazy model
public class Singleton1 {
private static Singleton1 instance = null;
// constructor must be private
private Singleton1() {
//
}
// return instance, if there is no instance create one, or just return field instance
// synchronized must add to prevent from thread safety problems
// and it must be a -- static function , class.function
public static synchronized Singleton1 getInstance() {
if(instance == null) {
instance = new Singleton1();
}
return instance;
}
// define other operations
}
2) 饿汉式
// an eager model
public class Singleton2 {
private static Singleton2 instance = new Singleton2();
// constructor
private Singleton2() {
//
}
// return instance
// and it must be a -- static function , class.function
public static Singleton2 getInstance() {
return instance;
}
// other operations
}
对于单例模式而言,不管采用何种实现方式,它都是只关心类实例的创建问题,不关心具体的业务功能。属于创建模式。
单例的范围:
通过实现可以看出,目前java里面实现的单例是一个虚拟机的范围,因为类装载是通过java vm在运行程序开始的时候通过classloader装载各个需要的类的,这就意味着如果有多个java vm同时运行一个单例程序时,将会实例化多个单例类。但是每个虚拟机中只有一个。
懒汉和饿汉的优缺点:
A) 懒汉是不到用时不创建,重视程序运行时的空间,但是如果是重量级的类,那么需要的时间会比较长。
B) 饿汉是一开始不管用不用,都先生成实例,这样用的时候就直接使用,如果不用的话,依然在内存中,占用系统空间,但是在使用时,可以直接用,比较重视时间。
一种更好的办法:利用单元素的枚举类型实现Singleton
Java枚举类型实质上是功能齐全的类;java枚举的基本思想是通过公有的静态final域为每个枚举常量导出实例的类;枚举是单例的泛型化,本质上是单元素的枚举。
public enum Singleton3 {
/*
*define a enum element, means a Singleton instance
* */
uniqueInstance;
// other operations can be added here
public void otherOpera() {}
}
通过对比,可以看出使用枚举实现单例控制更加简洁,而且无偿提供了序列化的机制,并由jvm提供了保证,高效、简洁、安全。