设计模式:单例模式
定义
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例并向整个系统提供这个实例。是所有设计模式中最简单的模式。
模式分析
目的
保证一个类只有一个实例,并且提供一个访问它的全局访问点。
代码实现
public class Singleton {
//静态私有成员变量
private static Singleton instance;
//私有构造函数
private Singleton(){}
//静态公有工厂方法,返回唯一的实例
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
在Singleton类中,成员变量和构造方法是私有的,外界类不能通过构造方法实例化Singleton对象(不能通过new来常见对象),getInstance()方法是静态公有的,外界类可以直接通过类名(Singleton)调用getInstance()方法。
getInstance()方法先判断instence是否为空,当instance == null 时才创建Singleton对象,否则直接返回静态变量instence,也就是说,只有Singleton对象被第一次创建时,才满足if条件,保证了类只有一个实例。getInstance()方法返回一个Singleton对象,是外界类获取Singleton对象的唯一方法,所以getInstance()方法是Singleton类提供的访问它的全局访问点。
优点
- 提供了唯一实例的受控访问。单例类可以严格可能控制客户何时以及怎样访问它。
- 节约系统资源。只有一个实例,节约资源,对于一些需要频繁创建和销毁的对象,单例模式可以提高系统的性能。
- 允许可变数目的实例。可以进行扩展,使用相似的方法指定对象实例的个数。
缺点
- 扩展困难。只有一个类,没有抽象层。
- 可能会造成资源无法及时释放。单例模式使用静态成员变量存储类的实例。(例如数据库的连接,需要及时释放资源)。
举例
在实际中,一台打印机可以连接多台设备,但是打印机在某一段时间只能完成一项打印任务。
public class Print {
//静态私有成员变量
private static Print instance = null;
//私有构造函数
public Print() {
}
//静态公有工厂方法,返回唯一的实例
public static Print getInstance(){
if(instance == null){
System.out.println("连接打印机");
instance = new Print();
}else{
System.out.println("打印机正在工作");
}
return instance;
}
}
public class Client {
public static void main(String[] args) {
Print p1,p2;
p1 = Print.getInstance();
System.out.println("--------------");
p2 = Print.getInstance();
}
}
在客户端测试代码中定义了两个打印任务,通过getInstance()两次获取对象实例,第二次时输出“打印机正在工作”,说明和第一次使用的是同一台打印机,所以打印机对象只被实例化了一次。
扩展
饿汉式单例
在初始化静态变量时创建对象,将变量设为static final(不可变),是JAVA语言中实现起来最方便的单例类。
private static final Singleton instance = new Singleton();
//私有构造函数
private Singleton(){}
//静态公有工厂方法,返回唯一的实例
public static Singleton getInstance(){
return instance;
}
在这个类被加载时,加载静态变量和静态方法,此时instance会被初始化,单例类的唯一实例被创建。
懒汉式单例
当第一次被引用时才创建实例。
//静态私有成员变量
private static Singleton instance =null;
//私有构造函数
private Singleton(){}
//静态公有工厂方法,返回唯一的实例
synchronized public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
使用了同步化机制(synchronized),用来处理多线程环境。
实践
编写Triple类,实现最多只能生成3个实例,实力编号分别为0,1,2,且可以通过getInstence(int id)来获取该编号的实例。
public class Triple {
private static Triple[] triple = new Triple[]{
new Triple(0),
new Triple(1),
new Triple(2),
};
private int id;
private Triple(int id) {
System.out.println("The instance " + id + " is created.");
this.id = id;
}
public static Triple getInstance(int id) {
return triple[id];
}
public String toString() {
return "[Triple id=" + id + "]";
}
}