单例模式是指在整个软件系统中,某个类只能产生一个对象实例,并且只有一个提供对象实例的方法。
很多人都说单例模式是最简单的一种设计模式,但是单例模式实际上有7种写法,很多人在面试的过程中会踩坑。接下来直接上代码。
class B{
private B(){}
private final static B b = new B();
public static B getInstance(){
return b;
}
}
优点:
在类装载的时候创建一次实例,避免了线程同步的问题
缺点:在类装载的时候就创建实例,因为类装载的原因有很多种,不一定是调用
getInstance方法才装载,没有达到懒加载的原则,如果从始至终没有使用过这个实例,就会造成内存浪费。
结论:这种方式可以用,但是有可能会造成内存浪费,不过大多数实际开发问题都不大。
class B{
private B(){}
private static B b ;
static {
b = new B();
}
public static B getInstance(){
return b;
}
}
饿汉
式
(静态代码块
)的写法和
饿汉
式
(静态方法
)的写法略有不同,但是他们的优缺点是一样的,仅仅是写法不一样。
class B{
private B(){}
private static B b ;
public static B getInstance(){
if(b == null){
b = new B();
}
return b;
}
}
缺点:在多线程的情况下,多个线程同时走到if(b==null)这一步有可能都返回true,于是就创建了多个实例。
class B{
private B(){}
private static B b ;
public synchronized static B getInstance(){
if(b == null){
b = new B();
}
return b;
}
}
这种方法是对
懒汉式(线程不安全)这种方法的一种优化,但是解决这个问题的同时又产生了另外的问题,即在多线程的情况下效率略低。
class B{
private B(){}
private static volatile B b ;
public static B getInstance(){
if(b == null){
synchronized (B.class){
if(b == null){
b = new B();
}
}
}
return b;
}
}
如上代码,对静态实例变量b加
volatile
修饰,多线程环境下就可以达到及时通知的目的,我们在获取实例的方法中加两个判空和一个同步,基本上达到了线程安全和效率的平衡点。
class B{
private B(){}
public static class C{
public static B b = new B();
}
public static B getInstance(){
return C.b;
}
}
如上代码:我们通过静态内部类C,在C中实例化对象B,再通过
getInstance获取到B的实例,因为类在装载的时候不会装载内部类,只有在使用
getInstance的时候才会装载,而且只会装载一次,而且不会产生线程问题。
enum B{
INSTANCE;
public void print(){
System.out.println("HELLO WORLD");
}
}
public class A {
public static void main(String[] args) {
B instance1 = B.INSTANCE;
B instance2 = B.INSTANCE;
System.out.println(instance1 == instance2);
System.out.println("instance1:"+instance1.hashCode());
System.out.println("instance2:"+instance2.hashCode());
instance1.print();
}
}
如上述代码和截图,可以看到 instance1和instance2的hascode都是一样的,足可以证明两个实例其实是一个,并且可以成功的调用到枚举类中的方法。
这是通过JDK1.5中的枚举来实现单例模式,能够避免多线程的问题,也是Effective java的作者提倡的方式。