单例模式主要符合单一职能原则。当一个类的职责是一定的,而且整个程序中不需要生成第二个此类的对象,而且如果生成第二个此类的对象的话还会有问题(比如我之前写的联棋游戏,里面的棋盘就应该始终只有一个对象,如果有两个会造成混乱),为了防止生成两个对象,而且节约内存,会使用单例模式。使用单例模式的典型场景比如数据库连接类,游戏主流程控制类等。
解决思路:
1.不允许其他程序使用new创建该类对象。(别人new不可控)
2.在该类中创建一个本类实例。
3.对外提供一个方法让其他程序可以获取该对象。
解决步骤:
1.私有化该类的构造函数
2.通过new在本类中创建一个本类对象。
3.定义一个共有的方法将创建的对象返回。
单例模式有三种类型:饿汉式、懒汉式、加强版的懒汉式。
一、饿汉式
public class Hungry {
// 将构造器置为私有
private Hungry() {
};
// 在本类中创建一个本类的对象
private static final Hungry single = new Hungry();
// 提供一个public方法,供其他类获得上面构造的对象
public static Hungry getInstance() {
return single;
}
//测试函数
public void connectDB() {
System.out.println("连接数据库成功!");
}
}
测试类:
public class Test {
public static void main(String[] args) {
// Hungry hungry=new Hungry();//如果这样写的话是有错误提示的,因为Hungry的构造函数是私有的。
Hungry.getInstance().connectDB();// 可以直接通过getInstance方法获得该类唯一的对象
}
}
二、懒汉式
public class Lazy {
//私有构造器
private Lazy() {};
//唯一对象
private static Lazy single=null;
//外部获得该对象的方法
public Lazy getInstance() {
if(single==null) {
single=new Lazy();
}
return single;
}
}
懒汉式将唯一类的生成从成员函数的初始化推迟到方法中。饿汉式中的single是在Hungry类被装载进jvm中就被初始化成类。而这里被初始化成null。
这样如果该对象没有被用到过,那么single就不会生成对象,尽最大可能节约内存。
而且因为single会在之后赋值,所以声明时没有使用final关键字。
但是如果在多线程中,这个方式可能会使内存中存在多个对象,所以这种方式一般很少用。比如执行完了if判断语句就转向其他线程的if语句了,然后两个都判断为false,那么接下来就会生成两个对象。
三、迭代后的懒汉式
为了解决这个方法,就使用同步,为getInstance方法添加关键字synchronized :
public synchronized Lazy getInstance() {
if(single==null) {
single=new Lazy();
}
return single;
}
但是这样每次调用此方法时都要判断锁,会严重降低效率,所以把这个关键字放在里面修饰语句:
public Lazy getInstance() {
synchronized(Lazy.class) {
if(single==null) {
single=new Lazy();
}
return single;
}
}
但其实好像并没有什么区别,还是每次调用的时候都要判断锁,如何做到只有当创建对象的时候才加锁,当对象不为空的时候就直接返回对象呢?最终的解决方案:
那就是在加锁前判断对象是否为Null:
public Lazy getInstance() {
if(single==null) {
synchronized(Lazy.class) {
if(single==null) {
single=new Lazy();
}
return single;
}
}
return single;
}
第一次判断是为了提升效率,当single对象已经生成就不需要检测锁,直接返回对象。
然后同步代码块保护线程安全,如果多个线程都运行了第一个判断后,进入到同步代码块,那么第二个if是为了这多个线程中只有一个生成对象,其他线程可以在第二个if外获得该线程生成的对象。