1.饿汉式
//饿汉式
public class Single implements Serializable {
private Single() {
//防止反射破坏单例
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("饿汉式单例无参构造方法~");
}
private static final Single INSTANCE = new Single();
public static Single getInstance() {
return INSTANCE;
}
//防止通过序列化反序列化获取对象
public Object readResolve() {
return INSTANCE;
}
}
2.懒汉式
public class Single1 implements Serializable {
private Single1() {
System.out.println("懒汉式式单例无参构造方法~");
}
private static volatile Single1 INSTANCE = null;//解决共享变量的可见性和有序性
//synchronized 防止多线程重复生成对象
public static Single1 getInstance() {
if (INSTANCE == null) {
//使用DCL(双检锁)提高性能
synchronized (Single1.class){
if (INSTANCE==null){
INSTANCE = new Single1();
}
}
INSTANCE = new Single1();
}
return INSTANCE;
}
//防止通过序列化反序列化获取对象
public Object readResolve() {
return INSTANCE;
}
}
3.使用普通类声明单例模式
破坏单例的三种情况
- 使用反射去使用构造函数,创建一个新的对象。
- 使用序列化反序列化的方式去获取新的对象
- 使用Unsafe的方式去获取对象
预防方法
-
防止通过反射的方法去获取单例对象,在构造函数中添加一个判断。
private Single() { //防止反射破坏单例 if (INSTANCE!=null){ throw new RuntimeException("单例对象不能重复创建"); } System.out.println("饿汉式单例无参构造方法~"); }
-
通过重写方法来预防序列化反序列化的方式获取单例对象。
//防止通过序列化反序列化获取对象 public Object readResolve(){ return INSTANCE; }
-
Unsafe目前无法预防
4.使用枚举类声明饿汉式
public enum SingleEnum {
INSTANCE;
SingleEnum() {
System.out.println("枚举类单例");
}
public static SingleEnum getInstance(){
return INSTANCE;
}
}
注:
当我们使用枚举类去声明单例模式时,并不需要手动去防止序列化反序列化以及反射去破坏单例,枚举类会自动为我们来防止这些,但是Unsafe还是无法防止。
5.使用内部类的方式实现懒汉式
在静态代码块中,代码是线程安全的,所以只要想办法吧代码放入静态代码块就可以避免很多麻烦的操作。
这里将声明放入到内部类中,可以简化我们的操作。
public class Single2 {
private Single2(){
System.out.println("懒汉式构造方法");
}
private static class Holder{
static Single2 INSTANCE = new Single2();
}
public static Single2 getInstance(){
return Holder.INSTANCE;
}
}
6.单例模式在jdk中的体现
例如:Runtime(饿汉式)、System中的Console(懒汉式DCL)、Collection中的ReverseComparator(内部类懒汉式)、Comparators(枚举饿汉式)