当前位置:网站首页>详解单例模式
详解单例模式
2022-06-30 17:32:00 【z啵唧啵唧】
文章目录
单例模式
1.饿汉式单例
特点:一上来先实例化对象
存在问题:可能会造成资源空间的浪费
/** * @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;
}
}
2.懒汉式单例
//---------------------基础版本单例模式,存在线程安全问题----------------------
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;
}
}
3.静态内部类获取单例
public class Holder {
/** * 构造器私有 */
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
/** * 静态内部类创建实例 */
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
4.反射破环单例模式
正常获取
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());
}
- 解决方法,改善私有构造器,三重检测锁的方式,我们在单例类的构造方法当中判断一下,instance1是否为空,如果不为空抛出异常
/** * 私有构造 */
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());
}
道高一尺魔高一丈!!!
5.枚举的方式,防止单例被破坏
/** * @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);
}
}
- 枚举类型最终的反编译后是有参构造器
- 反射破坏枚举抛出异常
边栏推荐
- What if icloud photos cannot be uploaded or synchronized?
- Force deduction solution summary 1175- prime number arrangement
- NFT挖矿游GameFi链游系统开发搭建
- Geoffrey Hinton:我的五十年深度学习生涯与研究心法
- 云安全日报220630:IBM数据保护平台发现执行任意代码漏洞,需要尽快升级
- EasyNVR平台设备通道均在线,操作出现“网络请求失败”是什么原因?
- Apple Watch无法开机怎么办?苹果手表不能开机解决方法!
- How to solve the lock-in read-only alarm of AutoCAD Chinese language?
- Oneortwo bugs in "software testing" are small things, but security vulnerabilities are big things. We must pay attention to them
- 教你30分钟快速搭建直播间
猜你喜欢
ASP. Net password encryption and password login
【TiDB】TiCDC canal_json的实际应用
漏洞复现----35、uWSGI PHP 目录遍历漏洞 (CVE-2018-7490)
Infineon - GTM architecture -generic timer module
Geoffrey Hinton: my 50 years of in-depth study and Research on mental skills
mysql for update 死锁问题排查
If you want to learn software testing, you must see series, 2022 software testing engineer's career development
OneFlow源码解析:算子签名的自动推断
Helping the ultimate experience, best practice of volcano engine edge computing
ONEFLOW source code parsing: automatic inference of operator signature
随机推荐
iCloud照片无法上传或同步怎么办?
[software testing] basic knowledge of software testing you need to know
Do you really understand the persistence mechanism of redis?
Digital intelligent supplier management system solution for coal industry: data driven, supplier intelligent platform helps enterprises reduce costs and increase efficiency
MySQL advanced - index optimization (super detailed)
Tensorflow2 ten must know for deep learning
How to do a good job in software system demand research? Seven weapons make it easy for you to do it
Rust 文件系统处理之文件读写 - Rust 实践指南
Troubleshooting MySQL for update deadlock
电子元器件行业在线采购系统精准匹配采购需求,撬动电子产业数字化发展
The new Post-00 Software Test Engineer in 2022 is a champion
C# Winform程序界面优化实例
剑指 Offer 16. 数值的整数次方
Compilation problems and solutions of teamtalk winclient
OneFlow源码解析:算子签名的自动推断
At present, the big guys are joining the two streams of flinksql, cdcmysql and Kafka, and the results are put into MySQL or KA
Glacier teacher's book
Research on the principle of Tencent persistence framework mmkv
NFT挖矿游GameFi链游系统开发搭建
Vs Code treeview TreeView