优点:懒加载(不用不占空间),正常方式获取的对象全局唯一,线程安全。
缺点:一个线程进入同步块,其余线程只能等待,浪费资源;可以使用反射和序列化破坏单例模式
import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class LazyDoubleCheckSingletonimplements Serializable{ //泛型要等到实例化阶段才能被确定,静态变量在类加载过程就已经初始化,加了泛型加载时无法确定类型,所以不能给静态变量加泛型 private volatile static LazyDoubleCheckSingleton instance; //数据存储 private T data; private LazyDoubleCheckSingleton(){} //私有化构造器,防止类外使用构造器实例化 public static LazyDoubleCheckSingleton getInstance(Class clazz){ //全局单例获取点 //检查是否要阻塞 if (instance == null){ synchronized (LazyDoubleCheckSingleton.class){ //检查是否要重新创建实例 if (instance == null){ instance = new LazyDoubleCheckSingleton (); } } } return instance; } public T getData() { return data; } public LazyDoubleCheckSingleton setData(T data) { this.data = data; return this; } } class Test01{ public static void main(String[] args) { for (int i = 0;i < 100;++i){ int j = i+1; new Thread(()->{ System.out.println(( "第"+j+"次获取对象的地址"+LazyDoubleCheckSingleton.getInstance(null))); }).start(); } } } class Test02{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor declaredConstructor = LazyDoubleCheckSingleton.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); System.out.println("反射对象的地址:"+declaredConstructor.newInstance()); System.out.println("正常获取的对象的地址:"+LazyDoubleCheckSingleton.getInstance(null)); } } class Test03{ public static void main(String[] args) throws IOException, ClassNotFoundException { byte[] bytes = null; ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(LazyDoubleCheckSingleton.getInstance(null)); bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); LazyDoubleCheckSingleton o = ((LazyDoubleCheckSingleton) new ObjectInputStream(bis).readObject()); System.out.println("正常获取的对象的地址:"+LazyDoubleCheckSingleton.getInstance(null)); System.out.println("序列化获取的对象的地址:"+o); } }
运行Test01的结果截图如下,多线程每次获取的对象都是同一个
运行Test02的结果如下图所示,使用反射和正常访问的对象不是同一个,所以反射可以破坏单例模式:
运行Test03的结果如下图所示,使用序列化和正常访问的对象不是同一个,所以反射可以破坏单例模式:
二、静态内部类
优点:懒加载(不用不占空间),正常方式获取的对象全局唯一;类加载器线程安全,不用手动处理线程同步;相比于double check多线程环境下线程不用等待。
缺点:可以使用反射(虽然可以用抛异常来处理)和序列化破坏单例模式
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyStaticInnerClassSingleton implements Serializable{
//内部类不使用就不加载
//私有化构造器
private LazyStaticInnerClassSingleton(){
if (LazyHolder.INSTANCE != null) throw new RuntimeException("不允许反射获取对象");
}
//全局唯一访问点
public static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
//静态内部类
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
class test01{
//多线程安全
public static void main(String[] args) {
for (int i = 0;i < 100;++i){
int j = i+1;
new Thread(()->{
System.out.println(( "第"+j+"次获取对象的地址"+LazyStaticInnerClassSingleton.getInstance(null)));
}).start();
}
}
}
class Test002{
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
Constructor declaredConstructor = LazyStaticInnerClassSingleton.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyStaticInnerClassSingleton lazyStaticInnerClassSingleton = declaredConstructor.newInstance();
System.out.println("正常获取的对象的地址:"+LazyStaticInnerClassSingleton.getInstance());
System.out.println("反射获取的对象的地址:"+lazyStaticInnerClassSingleton);
}
}
class Test003{
public static void main(String[] args) throws IOException, ClassNotFoundException {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(LazyStaticInnerClassSingleton.getInstance());
bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
LazyStaticInnerClassSingleton o = ((LazyStaticInnerClassSingleton) new ObjectInputStream(bis).readObject());
System.out.println("正常获取的对象的地址:"+LazyStaticInnerClassSingleton.getInstance());
System.out.println("序列化获取的对象的地址:"+o);
}
}
运行test01的结果截图如下,多线程每次获取的对象都是同一个
运行Test002的结果如下图所示,可以使用抛异常处理反射,如果不在构造器手动抛异常,则反射还是能成功构建对象,且破坏单例模式。
运行Test003的结果如下图所示,使用序列化和正常访问的对象不是同一个,所以序列化可以破坏单例模式:
三、枚举单例写法(终极武器,effective java一书推荐)优点:线程安全,不能使用反射和序列化破坏单例模式
缺点:无
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum EnumSingleton implements Serializable{
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
class Test05{
public static void main(String[] args) {
for (int i = 0;i < 100;++i){
int j = i+1;
new Thread(()->{
System.out.println(( "第"+j+"次获取对象的地址"+LazyDoubleCheckSingleton.getInstance(null)));
}).start();
}
}
}
class Test06{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor declaredConstructor = EnumSingleton.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingleton enumSingleton = declaredConstructor.newInstance();
System.out.println("正常获取的对象的地址:"+EnumSingleton.getInstance());
System.out.println("反射获取的对象的地址:"+enumSingleton);
}
}
class Test07{
public static void main(String[] args) throws IOException, ClassNotFoundException {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(EnumSingleton.getInstance());
bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
EnumSingleton o = ((EnumSingleton) new ObjectInputStream(bis).readObject());
System.out.println("正常获取的对象的地址:"+EnumSingleton.getInstance());
System.out.println("序列化获取的对象的地址:"+o);
}
}
1.线程安全:
2.不允许反射
3.序列化对象为同一个



