这周复习了一些基础的内容,其中有一条就是java如何实现单例的过程。
最基础的就是饿汉式。所谓饿汉式,就是在类加载时,就已经创建了单例,而不是在get方法执行时才创建。
public class Singleton1 implements Serializable {
private Singleton1() {
System.out.println(“private Singleton1()”);
}
private static final Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println(“otherMethod()”);
}
}
这种方法的问题在于,反射或者反序列化都可以破坏单例。
为了防止反射的破坏,可以在构造方法里添加null的判断。
public class Singleton1 implements Serializable {
private Singleton1() {
if(INSTANCE != null) {
throw new RuntimeException("单例不能重复");
}
System.out.println(“private Singleton1()”);
}
private static final Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println(“otherMethod()”);
}
}
如果想要反序列化,就需要加上readResolve() 方法
public class Singleton1 implements Serializable {
private Singleton1() {
if(INSTANCE != null) {
throw new RuntimeException("单例不能重复");
}
System.out.println(“private Singleton1()”);
}
private static final Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println(“otherMethod()”);
}
public Object readResolve() {
return INSTANCE;
}
}
第二种方法是枚举饿汉式
public enum Singleton2 {
INSTANCE;
private Singleton2(){
System.out.println(“private Singleton2()”);
}
@Override
public String toString() {
return getClass().getName() + “@“ + Integer.toHexString(hashCode());
}
public static Singleton2 getInstance(){
return INSTANCE;
}
public static void otherMethod(){
System.out.println(“otherMethod()”);
}
}
这种方法天然能够屏蔽反射破坏和反序列化破坏。
第三种,懒汉式。
public class Singleton3 implements Serializable {
private Singleton3() {
System.out.println(“private Singleton3()”);
}
private static final Singleton3 INSTANCE= null;
public static Singleton3 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton3();
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println(“otherMethod”);
}
}
这种如果是单线程则没有问题,但如果是多线程,可能两个线程同时进入到getInstance的if语句当中,导致重复创建单例。
为了解决这个问题,可以添加一个synchronized的锁,但与此同时出现的问题就是,只有在创建实例时需要锁,后续则会成为运行的负担。
第四种,双检懒汉式
public class Singleton4 implements Serializable {
private Singleton4() {
System.out.println(“private Singleton()”);
}
private static volatile Singleton4 INSTANCE = null;
public static Singleton4 getInstance() {
if (INSTANCE == null){
synchronized (Singleton4.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println(“otherMethod()”);
}
}
这种方式解决了普通懒汉式的问题,这个过程中单例一定要添加volatile的限制,保证单例在构造之后才会赋值,否则也可能出现空对象的情况。
5.内部类懒汉式
由于静态代码不存在重复创建的问题,所以把创建实例的功能放到内部的静态代码当中,这样就避免了双检的缺点了。
public class Singleton5 implements Serializable {
private Singleton5() {
System.out.println(“private Singleton5()”);
}
private static class Holder {
static Singleton5 INSTANCE = new Singleton();
}
public static Singleton5 getInstance() {
return Holder.INSTANCE;
}
public static void otherMethod() {
System.out.println(“otherMethod()”);
}
}