特点:一上来先实例化对象
存在问题:可能会造成资源空间的浪费
/**
* @Description: 饿汉式单例模式
* @Author:啵啵啵啵啵啵唧~~~
* @Date:2022/6/29
*/
public class Hungry {
/**
* 假如这个类在被创建的时候会开辟很大的数组
*
* 那么这个饿汉式单例模式存在的问题就是说可能会造成资源空间的浪费
* 比如现在就是这个类会创建很多的大数组,那么单例模式一上来就是实例化对像
* 如果这个对象后续没有被使用那么就浪费了很多的空间
* 所以根据饿汉式存在的问题引出懒汉式的单例模式
*/
private byte[] data1 = new byte[1024 * 1024];
private byte[] data2 = new byte[1024 * 1024];
private byte[] data3 = new byte[1024 * 1024];
private byte[] data4 = new byte[1024 * 1024];
/**
* 一个私有的构造方法
*/
private Hungry() {
}
/**
* 饿汉式单例,一上来不管三七二十一,一上来就先把对象进行加载
*/
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance() {
return HUNGRY;
}
}
//---------------------基础版本单例模式,存在线程安全问题----------------------
public class LazyMan {
/**
* 私有构造
*/
private LazyMan() {
}
/**
* 上来先不实例化对象
*/
private static LazyMan lazyMan;
/**
* 检测以下对象是否为空,当为空的时候才创建
* @return
*/
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
//-----------------------双重检测锁模式版本-----------------------------
public class LazyMan {
/**
* 私有构造
*/
private LazyMan() {
}
/**
* 双重检测锁对象我们给定为volatile进行修饰,防止指令重排,因为:
* lazyMan = new LazyMan(); 这个new对象的操作在底层并不是原子性的
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
* 假设现在A线程执行创建实例的代码,底层发生了指令重排 创建对象的三步顺序为 1 3 2
* 意味着线程A还没有初始化对象就指向了空间,假设此时有一个线程B执行到了这里
* 线程B在判断第一个lazyMan==null的时候就为false,因为线程A已经指向了内存空间
* 那么此时线程B就直接返回了这个对象,但是实际上线程A还没有初始化这个对象,所以返回的这个对象是虚无的
* 所以为了创建对象的时候发生指令重排,加一个volatile关键字修饰
*/
private volatile static LazyMan lazyMan;
/**
* 检测以下对象是否为空,当为空的时候才创建
* @return
*/
public static LazyMan getInstance() {
//这个第一个lazyMan == null 的作用是为了提高效率,不加这一层的话,每一次都要获得锁,获得锁实际上是很拉效率的操作
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
public class Holder {
/**
* 构造器私有
*/
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
/**
* 静态内部类创建实例
*/
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
public static void main(String[] args) {
LazyMan instance1 = LazyMan.getInstance();
LazyMan instance2 = LazyMan.getInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
public static void main(String[] args) throws Exception {
LazyMan instance1 = LazyMan.getInstance();
//获取单例类的构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
//无视私有的构造器
declaredConstructor.setAccessible(true);
//通过反射的方式获取实例
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
/**
* 私有构造
*/
private LazyMan() {
synchronized (LazyMan.class) {
if (lazyMan != null) {
//不为空还调用了构造器,说明被反射破坏了,抛出一个异常阻止
throw new RuntimeException("不要试图使用反射破环单例模式!!!");
}
}
}
public static void main(String[] args) throws Exception {
//获取单例类的构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
//无视私有的构造器
declaredConstructor.setAccessible(true);
//通过反射的方式获取实例
LazyMan instance1 = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
/**
* 红绿灯策略,防止单例杯破坏
*/
private static boolean target = false;
/**
* 私有构造
*/
private LazyMan() {
if (!target) {
target = true;
} else {
throw new RuntimeException("不要试图通过反射的方式破环单例");
}
}
public static void main(String[] args) throws Exception {
//获取单例类的构造器
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
//无视私有的构造器
declaredConstructor.setAccessible(true);
//获取单例类中的target字段
Field target = LazyMan.class.getDeclaredField("target");
target.setAccessible(true);
//通过反射的方式获取实例
LazyMan instance1 = declaredConstructor.newInstance();
//执行完毕之后,我们将字段的值改为false
target.set(instance1,false);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
道高一尺魔高一丈!!!
/**
* @Description: 枚举的方式防止单例被破坏
* enum本身也是一个类
* @Author:啵啵啵啵啵啵唧~~~
* @Date:2022/6/30
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}