# 1.饿汉式
# 代码示例1
public class SingletonHungry {
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry(){
}
private static SingletonHungry getInstance(){
return instance;
}
}
# 代码示例2
public class SingletonHungry {
private static SingletonHungry instance;
static {
instance = new SingletonHungry();
}
private SingletonHungry(){
}
private static SingletonHungry getInstance(){
return instance;
}
}
# 原理
# 2.懒汉式
# 代码示例
public class SingletonLazy {
private static volatile SingletonLazy instance;
private SingletonLazy(){
}
public static synchronized SingletonLazy getInstance(){
if(instance == null){
instance = new SingletonLazy();
}
return instance;
}
}
# 原理
# 3.双重检查锁
# 代码示例
public class DoubleCheckLock {
private static volatile DoubleCheckLock instance;
private DoubleCheckLock(){
}
public static DoubleCheckLock getInstance(){
if(instance == null){
synchronized (DoubleCheckLock.class){
if(instance == null){
instance = new DoubleCheckLock();
}
}
}
return instance;
}
}
# 原理
# 4.静态内部类
# 代码示例
public class SingletonStaticInner {
//私有构造器
//无法由外部创建此对象
private SingletonStaticInner(){
}
private static class Holder{
//创建外部类的实例对象, 如果实例对象构造复杂, 可以选择静态代码块构造
private static SingletonStaticInner instance = new SingletonStaticInner();
}
public static SingletonStaticInner getInstance(){
//调用静态内部类时, 构造外部类的实例对象, 并返回
//实现懒加载, 调用时才会构造静态内部类
return Holder.instance;
}
}
# 原理
# 静态内部类构造单例对象
public class SingletonStaticInner {
//单例对象的域引用
private SingletonClass singletonClass;
private SingletonStaticInner(){
//静态内部类在构造外部类时, 用私有构造器构造单例对象
singletonClass = new SingletonClass();
}
private static class Holder{
//静态内部类被调用时会去构造外部类的实例对象
//保证外部类一定是单例对象
private static SingletonStaticInner singletonStaticInner
= new SingletonStaticInner();
//或者使用静态代码块来构造外部类的单例对象
static {
//当外部类构造较为复杂时使用, 也可以在外部类的私有构造器中进行构造
}
}
//外部类调用此静态方法获取自己的单例对象
public static SingletonStaticInner getInstance(){
return Holder.singletonStaticInner;
}
//获取外部类中真实需要构造的实例对象
public SingletonClass get() {
return singletonClass;
}
}
# 5.枚举实现单例
枚举实现单例模式有其他模式不具备的好处:
- 能正常序列化和反序列化(避免序列化对单例的破坏)
- 反射无法创建对象
public enum SingletonEnum {
instance;
public void whateverMethod() {
//do something
}
}
# 使用枚举构造单例对象
public enum SingletonEnum {
//一个枚举实例,一定是一个唯一存在的对象
singleton;
//枚举实例中的域, 指向对应单例对象
private SingletonClass instance;
SingletonEnum(){
//初始化枚举类时, 创建或构造单例对象
this.instance = new SingletonClass();
}
public SingletonClass get() {
//提供获取单例对象的方法
return instance;
}
}
# 反射对单例的破坏
# 反序列化对单例的破坏
序列化与反序列化同样会对单例造成破坏, Java默认的反序列化时, 正是使用反射调用无参构造方法创建一个新的对象.
解决方案: 在Singleton类中定义readResolve方法, 直接return单例对象就可以了.
# 为什么反序列化不会破坏枚举实现的单例
原因如下: 在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。