其他分享
首页 > 其他分享> > 单例模式笔记

单例模式笔记

作者:互联网

目录

饿汉式

一开始就将对象初始化,不管用没用该类都去做分配空间,初始化对象,将对象指向空间

缺点:浪费空间

public class HungerMan {

	private HungerMan() {

	}

	private final static HungerMan hungerMan = new HungerMan();

	private static HungerMan getInstance() {
		return hungerMan;
	}

}

懒汉式

用到才去初始化对象,节约空间

缺点:只是用于单线程,程序要做并发则需要使用双检锁

public class LazyMan {
	private LazyMan() {	
	}
	
	private static LazyMan lazyMan ;
	
	private static LazyMan getInstance() {
		//为空时才去初始化对象
		if(lazyMan==null) {
			lazyMan = new LazyMan();
		}
		return lazyMan;
	}
	
	public static void main(String[] args) {
		for(int i=0;i<10;i++) {
			new Thread(()->{
				LazyMan.getInstance();
			}).start();
		}	
	}
}

双检锁 : 给上面代码增加一层锁,使用线程同步 synchronized

分析:

public class LazyMan {
	//私有构造器
	private LazyMan() {
		System.out.println(Thread.currentThread().getName()+"ok");
	}
	
	//加上volatile 原子性操作
	private volatile static LazyMan lazyMan ;
		
	private static LazyMan getInstance() {	
		/**
		 * 使用双检锁
		 */
		if(lazyMan==null) {
			synchronized (LazyMan.class) {
				if(lazyMan==null) {
					lazyMan = new LazyMan();
				}
			}
		}				
		return lazyMan;
	}
	
	public static void main(String[] args) {
		//1.
		for(int i=0;i<10;i++) {
			new Thread(()->{
				LazyMan.getInstance();
			}).start();
		}
		
		
		
	}
}

静态内部类

静态内部类为独立层,效果与双检锁一样

package workspace.单例模式;

//静态内部类
public class Inside {
	private Inside() {
		System.out.println(Thread.currentThread().getName() + "ok");
	}
	private static Inside getInstance() {
		return Man.INSIDE;
	}
	private static class Man {
		private final static Inside INSIDE = new Inside();
	}

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				Inside.getInstance();
			}).start();
		}
	}
}

枚举与反射

除了枚举所有的方法都是不安全的,都可以使用反射去破坏,不过反射是人为去破坏的,使用双检索或者静态内部类已经足够。

反射机制

通过反射可以直接构造空参函数

public static void main(String[] args) throws Exception{
		//正常实例化
		LazyMan lazyMan2 = LazyMan.getInstance();
		System.out.println(lazyMan2);
		//反射破坏
		Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
		constructor.setAccessible(true);
		LazyMan lazyMan = constructor.newInstance();
		System.out.println(lazyMan);

 	}

两个对象结果不一样反射成功破坏
在这里插入图片描述

解决方法:给空参函数加锁

private LazyMan() {
		synchronized (LazyMan.class) {
			if(lazyMan!=null) {
				throw new RuntimeException("不要视图通过反射破坏");
			}
			
		}
	}

破解:

初始化对象两个都为反射获取空参对象
在这里插入图片描述

对抗

可加入标志密钥防止放反射
在这里插入图片描述

但是这样依然是不安全的,因为可以通过jad反编译代码知道jingQing,将其修改回false反射仍然可以破坏。

枚举不可反射破坏

通过jad反编译枚举可以发现枚举中没有空参构造函数,因此枚举不能被破坏。
编写测试代码
在这里插入图片描述

返回结果不为
在这里插入图片描述
则不是我们想要的反射破坏失败结果
去反编译

再去破坏
在这里插入图片描述
反射不能破坏枚举单例模式

标签:反射,笔记,private,lazyMan,static,模式,单例,new,LazyMan
来源: https://blog.csdn.net/liaojsgtcg/article/details/121683141