单例模式有以下特点: 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。
《Effective Java》一书有一句话:单元素的枚举类型已经成为实现Singleton的最佳方法。
因此下次面试官让大家写单例模式的时候直接把他甩给面试官就可以啦!
枚举单例优点:
1、这种单例模式是线程安全的。
2、防止反射攻击:反射机制和序列化机制的情况下,其他方式的单例写法就无法做到单例类只能有一个实例这种说法了。事实上,通过Java反射机制是能够实例化构造方法为private的类的。这也就是我们现在需要引入的枚举单例模式。
相比于用懒汉、饿汉、双重检测、静态内部类等方式实现单例,简直简单太多了,「枚举」就是 JVM 提供给我们用来实现单例模式的一个语法糖。写法如下:
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
实现一个单例,应该着重关注下面两点:
- 构造函数需要是 private 访问权限,防止外部通过 new 关键字来创建对象实例。
- 创建对象时多线程的安全。
1--为什么它可以实现单例?
enum的构造方法天生就是private的(默认并且只能是private),也就是说这个构建枚举实例的过程不是我们做的(不允许被外界调用),是内部自动完成的,只要我们访问量枚举里面的实例,就会自动调用这个构造函数。同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。
2--为什么这种方法是线程安全的?
因为JVM保证枚举类的构造函数只会被调用一次。如果我们对枚举类型的代码进行反编译们就可以发现,他的构造函数经过反编译之后变成了static代码块了。
3--枚举可以防止反射攻击
在反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。所以枚举是不怕发射攻击的。
二、线程安全的单例写法1、饿汉式单例--线程安全
public class Singleton {
private Singleton() {}
private static final Singleton single = new Singleton();
public static Singleton getInstance() {
return single;
}
}
饿汉式是静态加载的时候实,不需要担心线程安全问题。
2、懒汉式单例--线程不安全
public class Singleton {
private Singleton() {}
private static Singleton single=null;
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
在并发的情况下是可能出现这种情况,就是a线程先进入getInstance()方法在创建实例化的时候,也就是还没创建成功,b线程也进入了getInstance()方法,这个时候a线程实例还没建成功,b线程判断single为空也开始创建实例,导致会出现创建出两个实例来。
解决方式有三种:
1、加上synchronized关键字,并发的时候也只能一个一个排队进行getInstance()方法访问。
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
2、双重检查锁定,这种方式会优于上面一种方式,在并发量高的情况下,不需要排队进getInstance()方法合理利用系统资源,性能上会优于上面一种。
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
3、静态内部类实现单例模式,这种方式优于上面两种方式,他即实现了线程安全,又省去了null的判断,性能优于上面两种。
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}