单例模式是java设计模式中最简单却最常用的一种设计模式
其含义就是在全局过程中只存在一个唯一实例
即要求:
- 不允许外界创建本类实例对象;
- 不允许外界修改本类唯一实例对象;
- 通过本类提供的方法去获取本类唯一的实例对象。
根据上述三点要求,可以以以下三点措施应对:
- 将本类构造函数私有化;
- 在本类中创建本类“私有”,“静态”实例对象;
- 创建“公有”,“静态”方法返回本类唯一静态实例对象。
举个例子,我们的巨硬的Windows的任务管理器,就是这样的一个单例模式,当我们右键任务栏打开任务管理器之后,再次右键打开,就会发现,置顶显示的依旧还是当前的任务管理器,有且只有一个。
所以新建一个任务管理器类叫做TaskManager
class TaskManager{
private static TaskManager instance=new TaskManager();//2.在本类创建静态私有实例对象;
private TaskManager(){//1.私有化构造方法使得不能在外部创建实例;
}
public static TaskManager getInstance(){//3.创建公有静态方法返回唯一实例对象
return instance;
}
public void showMessage(){//创建一个方法用来在main中测试本类实例对象
System.out.println("hello");
}
}
public static void main(string[] args){
TaskManager tm=TaskManager.getInstance();
tm.showMessage();
}
可以看到,在上面的代码中,在声明本类实例对象的同时直接进行了初始化,这样的方式叫做“饿汉式”,就是迫不及待的意思。
与之对应的是“懒汉式”,就是先声明,在getInstance()方法中判断如果为null,初始化之:
class TaskManager {
private static TaskManager tm;//声明的时候并没有初始化
// 需要把构造函数私有化,防止在类外随意创建本类对象
private TaskManager() {
}
public static TaskManager getInstance(){
// 把变量推迟初始化---懒汉式
if (tm == null) {
tm = new TaskManager();
}
return tm;
}
}
为什么要分这两种方法
这样做的好处是什么,坏处是什么
**饿汉式–在定义变量同时初始化的方式:优点:不存在多线程并发安全问题,缺点:可能导致内存浪费;
懒汉式–把变量推迟初始化:优点:相对节省内存,缺点:存在多线程并发安全问题。(可加锁,但效率低)**
线程安全问题很好理解,因为getInstance()方法中有判断是否为null的过程,如果有两个线程,其中一个线程走到判断是否为null时为true,进去了,正准备初始化,第二个线程抢到了资源,判断发现还没来得及初始化,也为null,一顿哐哐哐运行,实例化了一个对象,然后第一个线程接着开始走剩下的路,又实例化了一个对象,很好,两个实例对象出来了,单例模式失败。
所以有了一个叫做双检锁的方法(双重校验锁)
class TaskManager{
private volatile static TaskManager instance;
private TaskManager(){
}
public static TaskManager getInstance(){
if(instance==null){
synchronized(TaskManager.class){
if(instance==null){
instance=new TaskManager();
}
}
}
return instance;
}
public void showMessage(){
System.out.println("hello");
}
}
就是在第一次判断是否为null里面加锁之后,再加上一道判断,由于锁的连续性,不用担心第二次判断之后被抢走资源。
其实还有一种方法就是枚举!
枚举是JDK1.5之后的新特性,非常适合单例模式。
public enum TaskManager{
INSTANCE;
public void whateverMethod() { //随便什么方法
}
}
就是这么简单,但是用得却非常少。