设计模式——单例模式
什么是设计模式
设计模式简单来说就是工程师们经过长期编程,总结的一套固定的编程模式,就像盖房子,本来是随便盖的,但是经过长期实践,建筑者们形成了一套建筑的固定套路,比如哥特式建筑怎么建,四合院怎么建等等。
对应到程序员的软件工程行业,我们其实也是在建造“屋子”,这套设计模式就是我们编程的基本思路。
设计模式有很多种,这里介绍其中最简单也最常用的单例模式。
单例模式即,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。
单例模式实现
懒汉模式(单线程,类加载时不初始化,线程不安全)
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton () {
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton ();
}
return instance;
}
}
由于单例模式下,该对象只能生成一次,所以重构该类的默认构造方法为Private,但是这样又导致其他类无法访问,所以定义一个public的获取对象的方法getInstance(),该方法表明,当对象不存在时new一个类对象,否则返回对象,这样就实现了只能创建一个实例。
在其他类中,调用这个方法获取该类的实例。
测试类:
public class TestSingletom {
public static void main(String[] args) {
LazySingleton s1 = LazySingleton .getInstance();
LazySingleton s2 = LazySingleton .getInstance();
if(s1==s2){
System.out.println("创造的是相同的对象");
}else{
System.out.println("创造的是不同的对象");
}
}
}
//返回结果为:
创造的是相同的对象
懒汉模式进阶版本(单线程,单线程,类加载时不初始化)
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton () {
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton ();
}
return instance;
}
}
这里和前一个模式的唯一区别在于新加了一个synchronized,这是因为在多线程的情况下,如果两个线程同时运行到判断instance是否为null的if语句时,并且instance的确没有创建时,那么两个线程都会创建一个实例,此时Singleton就不能满足单例模式的要求了。
为了保证在多线程环境下我们还只能得到类的一个实例,需要加上一个同步锁。
饿汉模式(在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快)
public class EagerSingleton {
//饿汉单例模式
//在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
private static EagerSingleton instance = new EagerSingleton();
//静态私有成员,已初始化
private EagerSingleton()
{
//私有构造函数
}
public static EagerSingleton getInstance()
//静态,不用同步(类加载时已初始化,不会有多线程的问题)
{
return instance;
}
}
这种方法不会有线程问题,但是缺点是它不是一种懒加载模式式(lazyinitialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。
最优办法:静态内部类 实现单例模式
public class Singleton5 {
private static class SingletonHolder {
private static final Singleton5 INSTANCE = new Singleton5();
}
private Singleton5() {
}
public static final Singleton5 getInstance() {
return SingletonHolder.INSTANCE;
}
}
由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;
另外:
有人提问,例模式跟全局变量有什么区别?
他们的区别在于:全局变量是一个变量,存在于某个具体的类中,而单例是一个对象,可以存在于整个应用。