2015-09-02 16:02:07
推荐一篇大神的文章,好久之前就看过这篇文章,而且本文的思路就来自这篇文章~其实有了大牛的文章,本文可写可不写,但是为了给自己总结一下,就写了~
http://callmegod.iteye.com/blog/1474441
单例的作用想必大家都知道,也一直在用,但是到底怎样的单例是“比较”完美的单例模式呢?我也不知道,但是我一直在寻找答案~
下面我简单总结一下单例吧,否则太乱了
Part 1:单例的几种实现方式
1. 恶汉式:指在类加载或者被初始化的时候,就创建一个单例对象,有好几个变种
第一种:类加载时创建对象
1 public class SingletonObj { 2 public static final SingletonObj INSTANCE = new SingletonObj(); 3 4 private SingletonObj() {} 5 }
第二种:类加载时创建对象,但是对象是私有的,只能通过static的方法获取,好像更加“面向对象”
1 public class SingletonObj { 2 private static final SingletonObj INSTANCE = new SingletonObj(); 3 4 private SingletonObj() {} 5 6 public static SingletonObj getInstance() { 7 return INSTANCE; 8 } 9 }
第三种:类初始化的时候创建对象
1 public class SingletonObj { 2 public static SingletonObj INSTANCE; 3 4 static { 5 INSTANCE = new SingletonObj(); 6 } 7 8 private SingletonObj() {} 9 10 private static SingletonObj getInstance() { 11 return INSTANCE; 12 } 13 }
2. 懒汉式:使用时才创建对象,相比于恶汉式,这种方式会延迟加载。
第一种:线程不安全
1 public class SingletonObj { 2 public static SingletonObj INSTANCE; 3 4 private SingletonObj() {} 5 6 private static SingletonObj getInstance() { 7 if (INSTANCE == null) { 8 INSTANCE = new SingletonObj(); 9 } 10 return INSTANCE; 11 } 12 }
第二种:线程安全,同时支持双重检查机制,这种也是最常见的单例模式(这种方式不推荐)
1 public class SingletonObj { 2 private static final Object sLocker = new Object(); 3 public static SingletonObj INSTANCE; 4 5 private SingletonObj() { 6 } 7 8 private static SingletonObj getInstance() { 9 if (INSTANCE == null) { 10 synchronized (sLocker) { 11 if (INSTANCE == null) { 12 INSTANCE = new SingletonObj(); 13 } 14 } 15 } 16 return INSTANCE; 17 } 18 }
这种方式有自己的弊端:有种说法,说是jdk1.5后才能保证达到单例效果,但是据我所知,这种方式来自C语音的双重检查机制,在Java中不见得能完全适用。
所以保险的加锁方式如下:
1 public class SingletonObj { 2 public static SingletonObj INSTANCE; 3 4 private SingletonObj() { 5 } 6 7 private synchronized static SingletonObj getInstance() { 8 if (INSTANCE == null) { 9 INSTANCE = new SingletonObj(); 10 } 11 return INSTANCE; 12 } 13 }
3. 静态内部类方式:加锁的方式能做到线程安全,但是加锁会导致效率比较低,而这种方式既能满足线程安全的要求,而且效率比较高
1 public class SingletonObj { 2 private static class SingletonHolder { 3 public static final SingletonObj INSTANCE = new SingletonObj(); 4 } 5 6 private SingletonObj() { 7 } 8 9 private static SingletonObj getInstance() { 10 return SingletonHolder.INSTANCE; 11 } 12 }
主要利用到静态类只会被JVM加载一次的特性~所以即便是多线程,也不会产生两个对象。这也是一种比较优雅的方式。
4. 《Effective Java》中提到一种更好的方法:单元素的枚举类型,大体实现如下(极棒的方式)
1 public enum SingletonEnum { 2 INTANCE(12, "Niu"); 3 4 private int age; 5 private String name; 6 SingletonEnum(int age, String name) { 7 this.age = age; 8 this.name = name; 9 } 10 11 public void doSomething() { 12 System.out.println("SingletonEnum doSomething"); 13 } 14 }
好处:线程安全,序列化/反序列化安全
弊端:失去了类的一些特性,因此不够流行
Part 2:
1. 上面提到了序列化,我们都知道,所谓序列化/反序列化就是对象和流之间的一种转换方式。因为对象是不能直接保存在硬盘或者通过网络传输的,但是序列化成流之后,原则上你想干嘛都行。但是单例的序列化比较特殊,我们知道,单例的作用就是保证JVM中只有一个对象,那么如何保证反序列化后的对象和序列化之前的是同一个对象呢?
1 public class SingletonCase implements Serializable { 2 private static class SingltonHolder { 3 public static final SingletonCase INSTANCE = new SingletonCase(); 4 } 5 6 private int age; 7 private String name; 8 public static SingletonCase getInstance() { 9 return SingltonHolder.INSTANCE; 10 } 11 12 private SingletonCase() { 13 age = 133; 14 name = "David"; 15 } 16 17 public int getAge() { 18 return age; 19 } 20 21 public String getName() { 22 return name; 23 } 24 25 private Object readResolve() { 26 return SingltonHolder.INSTANCE; 27 } 28 }
需要添加如上绿色代码,这是一个方法,返回值必须是Object,而不能是自己定义的对象。这个方法的作用是:每次反序列化完成前都会去调用readResolve()方法,将用原对象取代新创建出来的对象,这样就保证了序列化前后对象的唯一性。
单例的线程安全和序列化一直是个比较难缠的安全问题,所以单元素的枚举类型实现单例,既能保证线程安全性,又能保证序列化安全,可谓是一种perfect的方案~~
2. 关于transient
如果你不想某个成员变量被序列化,那么就可以用这个关键字来修饰~具体的使用大家问度娘。