-
单例设计模式
设计模式:解决某一问题最行之有效的方法。Java中有23种设计模式。
单例设计模式:解决一个类在内存中只存在一个对象。
想要保证对象唯一,有如下要求:
1、为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象;
2、还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对象;
3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
这三个要求的代码实现:
1、将构造函数私有化;
2、在类中创建一个本类对象;
3、提供一个方法可以获取到该对象。
代码:
class Single{
private Single() {} //构造函数私有化
private static Single s = new Single(); //在本类中创建一个本类对象
//提供一个方法获取该对象,由于在其它类中不能创建对象,不能调用获取getInstance方法,所以需要将该方法用static修饰,从而使用类名调用方法,同时上面的Single对象也要用static修饰(静态方法只能使用静态成员)
public static Single getInstance() {
return s;
}
}
class SingleTest{
public static void main(String[] args) {
Single ss = Single.getInstance();
}
}
内存分析如下图:
首先进入main方法,将main方法压入栈,并加载Single类,将静态成员、静态方法以及方法放入方法区;
加载Single类时,新建了一个类对象Single,假设地址为0x0045,并且静态成员s指向该对象;
在main方法中,调用getInstance方法,获得了类中建立的s指向的对象,将ss也指向该对象;
这时假设再次调用getInstance方法,获得了类中建立的s指向的对象,将s1也指向该对象;
因此ss与s1指向的是同一对象,这就实现了一个类在内存中只存在一个对象。
对于事物该怎么描述还是怎么描述,当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。实例如下:
class Single{
private int num;
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
private Single() {}
private static Single s = new Single();
public static Single getInstance() {
return s;
}
}
class SingleTest{
public static void main(String[] args) {
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
s1.setNum(23);
System.out.println(s2.getNum()); //得到的结果为23,因为s1和s2指向的是同一个对象
}
}
-
单例设计模式方式二
单例设计模式有两种:饿汉式和懒汉式。
//这个是先初始化对象,称为:饿汉式
/*class Single{
private Single() {}
private static Single s = new Single();
public static Single getInstance() {
return s;
}
}*/
//这个是方法被调用时,对象才被初始化,也叫做对象的延时加载,称为:懒汉式。
//Single类进入内存后,对象还没有存在,只有调用了getInstance方法时,才建立对象。
class Single{
private Single() {}
private static Single s = null;
public static Single getInstance() {
if(s==null)
s = new Single();
return s;
}
}
class SingleTest{
public static void main(String[] args) {
Single s1 = Single.getInstance();
}
}
但是上面的懒汉式会出现不安全的问题,因为如果是单核CPU,并行则不是真正意义上的并行,这时如果调用了三次getInstance方法,假设为A,B,当A运行到if(s==null)之后突然停下来了,则当前s仍为null,这时又来了一个B,运行到if(s==null)之后也突然停下来了,则当前s仍为null,这是A恢复过来,创建了一个对象,然后B又恢复过来,也创建了一个对象,则此时一个类中建立了两个对象,不具有了唯一性,就不是单例设计模式了。
对于这种不安全的问题,可以利用同步关键字来实现互锁,解决方法如下:
public static synchronized Single getInstance() {
if(s==null)
s = new Single();
return s;
}
这是同时来了A,B,A进入方法之后,便上了锁,之后B来了判断锁住了,便不能进入该方法,但是上述方法每次都要判断是否为上锁状态,所以运行较慢,优化运行时间可以采用一下方式(双判断):
public static Single getInstance() {
if(s==null){
synchronized(Single.class){
if(s==null)
s = new Single();
}
}
return s;
}
采用该改进之后,假设来了A,B,C,这是时A进入了方法,判断s==null,且未锁,则进入互锁的内部,这时卡住了的话,这时来了B,判断s==null,且已锁,则等待;等A结束且建立了一个对象,此时s!=null,这时C进入方法直接判断s是否为null,条件不满足,直接返回已创建好的Single对象,不会再创建新的Single对象。
相比于饿汉式,这个方法的代码比较多,因此定义单例设计模式,建议使用饿汉式。