一个好的代码,绝对不是靠数量堆上去的,而是靠一个好的,清晰的逻辑,来构建的。就像平常的修房子一样,要先设计好,而不是直接就扛着砖直接盖。在java中的常用的设计模式有,单例模式,装饰者模式,观察者模式,外观模式,工厂模式,模板模式等。在接下来的一个星期内,会好好的介绍一下,自己对于这些设计模式的理解。今天我们先从最简单的单例模式开始。
很多时候,有些类我们只需要一个实例对象,多了会有问题,像是我们聊天室的管理员,我们的硬件设备打印机等等。那么此时单例模式就提供了一个很好的实现。
以前我们平常写一个类,在外面可以new出多个对象。
public class A { } public void getA(){ A a1= new A(); A a2 = new A(); }
我们有一个A的类,它可以得到a1,a2两个不同的对象他们分别指向不同的地址值。
private A(){
}
}
我们可以通过这样将类私有化之后,那么就不能再外面调用对象。为了得到内部的对象和保证是单一的那么此时,经典的单例模式就出现了。在这里我们以一个用户管理中心为例。
//私有化构造是外界不能创建
private UserManage(){
}
//内部进行创建,并将方法,设为共有,供外部调用
public static UserManage getInstance(){
if(um==null){
um = new UserManage();
}
return um;
}
}
通过静态加载,可以控制它的一个的对象的个数;将构造器私有,只有内部才能调用;最后我们使用getInstance方法,在内部并且当这个方法第一次被调用时,new出对象,并且方法为public 课供外部调用。
上面所说的都是基本的,经典的单例模式,但是这个是有缺陷的。
在多线程的情况下,可能多个线程在同一个时间片调用了,getInstance方法,在第一个线程进去的时候,可能第二个也进去了,这时候,第一的对象没有new出来,这时判断也就失效了第二个也new出了对象。这就出现了bug,基于这种比较极端的情况下出现的bug,下面是一些改进的方法。
方法一:使用同步锁synchronized
public static synchronized UserManage getInstance(){ if(um==null){ um = new UserManage(); } return um; }
加上同步锁之后,只能一个线程调用这个方法,前面的以外也就不会出现了。但是,大家都知道,线程锁是非常消耗资源的,如果我们在外面经常调用,就会大大降低程序的性能,当然如果在外面这个方法,调用的比较少就没太大问题呢。
方法二:“急切”创建法
private UserManage(){
}
public static UserManage getInstance(){
return um;
}
就是在加载类的时候,直接把对象创建出来,就是说类一被加载完成,那么这个对象也就存在了,但是这样就会浪费一些内存资源,可能你在很长时间内都用不到这个方法,或是这个类本身就比较大时是对于资源的浪费。
方法三:双重检查加锁法
private UserManage(){
}
public static UserManage getInstance(){
if(um==null){
synchronized (UserManage.class){
if(um==null){
um = new UserManage();
}
}
}
return um;
}
此方法也是通过也是通过线程锁来解决,但是这里将线程锁加在第二个判断语句处,当遇到,两个或是多个同时进来的时候,会同时经过那个线程锁,但是只有第一个调用的线程会启动线程锁,在一个整个工程都不会再次用到线程锁,只会在第一次加载的时候使用。这样也很好的保证了,线程的原子性。但是,这样在第一次调用的时候都会加载线程锁,若是这个方法,自始至终都只用到一次,那么也是算浪费了资源。
其实上面介绍了几种对于单例模式的改进,每种都有各自的适用场景。在调用多的时候可以考虑第二,三种,调用次数少时可以考虑第三种,第一种。当然若不是多线程也可以考虑经典模式。不同的场景,应用不同的方法。