单例模式是指在整个软件系统中,某个类只能产生一个对象实例,并且只有一个提供对象实例的方法。
很多人都说单例模式是最简单的一种设计模式,但是单例模式实际上有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的作者提倡的方式。