单例
饿汉模式一
package com.design_mode.singleton;
import java.io.Serializable;
/**
* @author liu yue
* @description: 单例模式1:
* 饿汉模式
* 1.构造私有
* 2.类加载时初始化静态私有单例对象
* 3.提供一个获取实例的静态方法
*
* TODO
* 破坏单例模式
* 1.反射, 使用构造方法,
* 预防方法: 构造方法中抛异常
* 2.实现了序列化接口的类可以被反序列化, 不走构造方法,通过流创建实例
* 预防方法: 重写readResolve方法,反序列化时,发现重写了方法,以当前方法的返回值进行返回, 如果发现是枚举类时, 反序列化时,直接返回枚举,不会创建对象
* 3.unsafe进行破坏, 不走构造方法
* 预防方法: 目前没有搜到有关资料
*
* @date 2022/7/7 19:27
*/
public class Singleton1 implements Serializable {
private Singleton1(){
//TODO 防止反射创建,破坏单例模式
if (SINGLETON_1 != null){
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("private Singleton1 init...");
}
private static final Singleton1 SINGLETON_1 = new Singleton1();
public static Singleton1 getSingleton1Instance(){
return SINGLETON_1;
}
public void go(){
System.out.println("Singleton1 go...");
}
//TODO 防止反序列化生成对象,破坏单例模式
public Object readResolve(){
return SINGLETON_1;
}
}
饿汉模式二
package com.design_mode.singleton;
/**
* @author liu yue
* @description: 枚举类实现饿汉单例模式: 类加载就被创建
* 枚举类在编译时期,会继承Enum<?>父类,被添加static代码块,创建实例对象
* 这里的ordinal就是S1与S2的先后顺序
* 字节码部分内容:
* public static final Singleton2_enum S1;
* public static final Singleton2_enum S2;
* private Singleton2_enum(String name, int ordinal){ super(name,ordinal}
* static{
* Singleton2_enum S1 = new Singleton2_enum("S1",0);
* Singleton2_enum S2 = new Singleton2_enum("S2",1);
* }
*
* TODO 单例模式破坏情况
* 1.反序列化: 不会破坏, 因为ObjectInputStream中反序列化时,对枚举类进行了优化处理, 遇见了枚举类,直接返回其实例对象
* 2.反射: 不能破坏, 因为枚举只有私有有参构造, 且构造不允许反射创建
* @date 2022/7/7 20:10
*/
public enum Singleton2_enum {
S1;
//,S2;
//看看无参执行了吗
Singleton2_enum(){
System.out.println("Singleton2_enum init...");
}
//可以不提供获取方法, 枚举就是公共的可获取的
public static Singleton2_enum getInstance1(){
return S1;
}
// 可以不提供获取方法, 枚举就是公共的可获取的
/* public static Singleton2_enum getInstance2(){
return S2;
}*/
//重新toString, 打印当前对象
//不重写时,打印的就是 S1 或 S2 对象的名字
@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public static void go(){
System.out.println("enum go....");
}
}
懒汉模式一: dcl
package com.design_mode.singleton;
import java.io.Serializable;
/**
* @author liu yue
* @description: 单例模式_懒汉模式 DCL: double check lock 双检锁
* @date 2022/7/7 20:56
*/
public class Singleton3 implements Serializable {
private static final Object object = new Object();
//TODO volatile在这里主要用于禁止指令重排序
private static volatile Singleton3 SINGLETON_3;
private Singleton3() {
//TODO 预防反射创建对象
if (SINGLETON_3 != null){
throw new RuntimeException("单例模式,不允许反射创建对象");
}
System.out.println("singleton init...");
}
public static Singleton3 getSingleton3() {
//TODO double check 外层判断防止每次进入方法都抢锁
if (SINGLETON_3 == null) {
synchronized (object) {//加锁
//TODO 内存判断,防止多个线程进行创建对象,因为可能多个线程进入第一个if,被卡在锁外
if (SINGLETON_3 == null) {
//TODO volatile 禁止指令重排序
// 如果此时正在创建对象, 发生了指令重排,导致先给SINGLETON_3赋值地址,
// 但其内部并未完成初始化,此时另外的线程可能拿着这个未初始化的对象进行操作,引发异常
SINGLETON_3 = new Singleton3();
}
}
}
return SINGLETON_3;
}
//TODO 预防反序列化破坏单例
public Object readResolve(){
return SINGLETON_3;
}
}
单例模式二: 内部类
package com.design_mode.singleton;
/**
* @author liu yue
* @description: 单例模式 内部类 懒汉模式
* @date 2022/7/8 0:18
*/
public class Singleton4 {
private Singleton4(){
System.out.println("single4 init...");
}
//内部类的静态属性, 如果没有使用到内部类, 内部内属性不会初始化
private static class Holder{
static Singleton4 singleton4 = new Singleton4();
}
//获取实例时, 由于Holder中的实例是静态的, jvm保证了线程安全
public static Singleton4 getInstance4(){
return Holder.singleton4;
}
public void go(){
System.out.println("singleton4 go...");
}
}
破坏单例
package com.design_mode.singleton;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import java.io.*;
import java.lang.reflect.Constructor;
/**
* @author liu yue
* @description: 破坏单例模式
* @date 2022/7/7 19:42
*/
public class DestroySingleton {
//TODO 反射. 可以被预防
public static Object reflect(Class<?> clazz) throws Exception {
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
}
//TODO 反射创建枚举类对象不行,
public static Object reflect2(Class<?> clazz,int i) throws Exception {
//即使走有参构造也不行
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
return constructor.newInstance("other", i);
}
//TODO 序列化 不走构造方法. 可以被预防
public static Object serializable(Object instance) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(instance);
//TODO 解析枚举类对象时, ObjectInputStream会对枚举进行处理, 直接返回枚举类的对象; 而不会使用反序列化数组生成的结果
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
return ois.readObject();
}
//TODO unsafe破坏. 目前没搜索到预防方法
public static Object unsafeDestroy(Class<?> clazz) throws Exception {
//使用Spring的工具类,获取Unsafe对象实例, 并根据class对象创建实例
return UnsafeUtils.getUnsafe().allocateInstance(clazz);
}
}
测试单例
饿汉模式1测试
package com.design_mode.singleton;
/**
* @author liu yue
* @description: 单例模式1 普通单例模式 饿汉模式1测试
* @date 2022/7/7 21:06
*/
public class Singleton1_Test {
public static void main(String[] args) throws Exception {
//TODO 测试饿汉单例1 反射 序列化 unsafe都能破坏单例模式1, 反射和序列化可以预防, unsafe不能预防
Singleton1 singleton1 = Singleton1.getSingleton1Instance();
System.out.println(singleton1);//获取单例1
System.out.println(singleton1);//检验单例
try {
System.out.println(DestroySingleton.reflect(singleton1.getClass()));//测试反射
}catch (Exception e){
System.out.println("反射失败");
e.printStackTrace();
}
System.out.println("反序列化破坏饿汉模式1: " +
DestroySingleton.serializable(Singleton1.getSingleton1Instance()));//测试反序列化
System.out.println("unsafe破话饿汉模式1: " +
DestroySingleton.unsafeDestroy(singleton1.getClass()));//测试unsafe破坏. 目前没搜到预防手段
System.out.println();
}
}
饿汉模式2测试
package com.design_mode.singleton;
/**
* @author liu yue
* @description: 单例模式2 枚举类 饿汉单例模式2测试
* @date 2022/7/7 21:08
*/
public class Singleton2_enum_Test {
public static void main(String[] args) throws Exception{
//TODO 测试饿汉单例2: 枚举 反射和序列化都不能破坏枚举的单例模式, unsafe依然能够破坏枚举单例模式
Singleton2_enum s1 = Singleton2_enum.S1;
System.out.println(Singleton2_enum.S1);//获取实例
System.out.println(Singleton2_enum.S1);//测试两次获取的是否为同一个
try {
//TODO 枚举类没有无参构造, 不能反射创建,
System.out.println(DestroySingleton.reflect(s1.getClass()));//反射不能破坏枚举的单例模式
}catch (Exception e){
System.out.println("反射1失败");
e.printStackTrace();
}
try {
//TODO 使用有参构造时, 也会被抛出异常: 非法参数异常: 不能使用反射创建枚举
System.out.println(DestroySingleton.reflect2(s1.getClass(), 1));//反射不能破坏枚举的单例模式
}catch (Exception e){
System.out.println("反射2失败");
e.printStackTrace();
}
//TODO 反序列化,不能破坏枚举的单例模式
System.out.println("反序列化枚举: " + DestroySingleton.serializable(s1));
//TODO unsafe依然能够破坏枚举单例
System.out.println("unsafe枚举: " + DestroySingleton.unsafeDestroy(s1.getClass()));
System.out.println();
}
}
懒汉模式1 dcl测试
package com.design_mode.singleton;
/**
* @author liu yue
* @description: 单例模式3 普通单例 懒汉模式1测试 DCL: double check lock 双检锁
* @date 2022/7/7 21:11
*/
public class Singleton3_Test {
public static void main(String[] args) throws Exception{
//TODO 测试饿汉单例1 反射 序列化 unsafe都能破坏单例模式1, 反射和序列化可以预防, unsafe不能预防
Singleton3 singleton3 = Singleton3.getSingleton3();
System.out.println(singleton3);//获取单例1
System.out.println(singleton3);//检验单例
try {
System.out.println("反射创建实例: " + DestroySingleton.reflect(singleton3.getClass()));//测试反射
}catch (Exception e){
System.out.println("反射失败");
e.printStackTrace();
}
System.out.println("反序列化破坏饿汉模式1: " +
DestroySingleton.serializable(Singleton3.getSingleton3()));//测试反序列化
System.out.println("unsafe破坏饿汉模式1: " +
DestroySingleton.unsafeDestroy(singleton3.getClass()));//测试unsafe破坏. 目前没搜到预防手段
System.out.println();
}
}
jdk源码中的单例
饿汉
- System.exit();
- System.gc();
- Collections中的很多空集合 EMPTY开头的集合
- 枚举类饿汉: Collections中的内部类ReverseComparator中的reversed方法获取一个正序比较器,
其获取的return Comparator.naturalOrder() —>
(Comparator) Comparators.NaturalOrderComparator.INSTANCE —> Comparators的内部枚举类
懒汉
- 1.双检锁: System中的Console控制台对象
- 2.Collections中的很多空集合 EMPTY开头的集合