首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,595 阅读
2
主板开机跳线接线图【F_PANEL接线图】
7,388 阅读
3
Linux使用V2Ray 原生客户端
6,472 阅读
4
移动光猫获取超级密码&开启公网ipv6
5,358 阅读
5
NVIDIA 显卡限制功率
3,184 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
358
篇文章
累计收到
72
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
32
篇与
的结果
2021-12-13
设计模式学习笔记2:单例模式
1.介绍单例模式的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。这种类型的设计模式属于创建型模式。单例模式具有典型的三个特点:只有一个实例。自我实例化。提供全局访问点。应用实例:1、一个班级只有一个班主任。2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。优点:1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。2、避免对资源的多重占用(比如写文件操作)。缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。使用场景:1、要求生产唯一序列号。2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。2.几种实现方式2.1 懒汉式,线程不安全是否 Lazy 初始化:是是否多线程安全:否描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。// 懒汉式单例 public class LazyMan { // 1.私有化构造函数 private LazyMan(){} // 2.先定义单例对象但不实例化 private static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ if (lazyMan==null){ lazyMan = new LazyMan(); } return lazyMan; } }2.2 懒汉式,线程安全是否 Lazy 初始化:是是否多线程安全:是描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。优点:第一次调用才初始化,避免内存浪费。缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。// 懒汉式单例 public class LazyMan { // 1.私有化构造函数 private LazyMan(){} // 2.先定义单例对象但不实例化 private static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static synchronized LazyMan getInstance(){ if (lazyMan==null){ lazyMan = new LazyMan(); } return lazyMan; } }2.3 饿汉式是否 Lazy 初始化:否是否多线程安全:是描述:这种方式比较常用,但容易产生垃圾对象。优点:没有加锁,执行效率会提高。缺点:类加载时就初始化,浪费内存。// 饿汉式单例模式 public class Hungry { // 1.私有化构造函数 private Hungry(){} // 2.事先创建好对象 private final static Hungry HUNGRY = new Hungry(); // 3.当需要调用的单例对象的时候通过暴露的公有的方法进行调用 public static Hungry getInstance(){ return HUNGRY; } }2.4 双检锁/双重校验锁(DCL,即 double-checked locking)/DCL懒汉式单例是否 Lazy 初始化:是是否多线程安全:是实现难度:较复杂描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。// 懒汉式单例 public class LazyMan { // 1.私有化构造函数 private LazyMan(){} // 2.先定义单例对象但不实例化 private volatile static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ // 双重检测锁模式的懒汉式单例 DCL懒汉式 if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } }为什么要加volatilelazyMan = new LazyMan(); // 不是一个原子性操作创建对象的过程不是一个原子操作,分为以下3步:1.分配内存空间2.执行构造方法,初始化对象3.把这个对象指向这个空间在执行的过程中可能会发生指令重排的现象,即我们真实想要的执行顺序是1->2->3。但是可能真实的执行顺序是1->3->2。则当线程A以1->3->2的顺序执行到3时,线程B也调用了返回对象实例的方法,则给线程B可能拿到未创建完成的对象进行调用从而导致出错。2.5 登记式/静态内部类是否 Lazy 初始化:是是否多线程安全:是描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }2.6 枚举描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。public enum Singleton { INSTANCE; public void whateverMethod() { } }3.反射下的单例模式3.1 V1:在单例类已经实例化了对象的条件下使用反射破坏单例3.1.1 示例package single; import java.lang.reflect.Constructor; // 懒汉式单例 public class LazyMan { // 1.私有化构造函数 private LazyMan(){} // 2.先定义单例对象但不实例化 private volatile static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ // 双重检测锁模式的懒汉式单例 DCL懒汉式 if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); // 获取空参构造器 Constructor<LazyMan> declareConstructor = LazyMan.class.getDeclaredConstructor(null); // 将空参构造器的Accessible设置为true declareConstructor.setAccessible(true); // 调用空参构造器实例化对象 LazyMan instance2 = declareConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } } single.LazyMan@1b6d3586 single.LazyMan@4554617c3.1.2 防止措施通过在构造函数中加入一个同步代码块进行一次是否已经生成了该单例类对象的判断package single; import java.lang.reflect.Constructor; // 懒汉式单例 public class LazyMan { // 1.私有化构造函数 private LazyMan(){ synchronized (LazyMan.class){ if(lazyMan!=null){ throw new RuntimeException("不要试图使用反射破坏单例模式"); } } } // 2.先定义单例对象但不实例化 private volatile static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ // 双重检测锁模式的懒汉式单例 DCL懒汉式 if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); // 获取空参构造器 Constructor<LazyMan> declareConstructor = LazyMan.class.getDeclaredConstructor(null); // 将空参构造器的Accessible设置为true declareConstructor.setAccessible(true); // 调用空参构造器实例化对象 LazyMan instance2 = declareConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at single.LazyMan.main(LazyMan.java:38) Caused by: java.lang.RuntimeException: 不要试图使用反射破坏单例模式 at single.LazyMan.<init>(LazyMan.java:11) ... 5 more3.2 V2:V1解决方案破解3.2.1 示例package single; import java.lang.reflect.Constructor; // 懒汉式单例 public class LazyMan { // 1.私有化构造函数 private LazyMan(){ synchronized (LazyMan.class){ if(lazyMan!=null){ throw new RuntimeException("不要试图使用反射破坏单例模式"); } } } // 2.先定义单例对象但不实例化 private volatile static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ // 双重检测锁模式的懒汉式单例 DCL懒汉式 if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) throws Exception { // 获取空参构造器 Constructor<LazyMan> declareConstructor = LazyMan.class.getDeclaredConstructor(null); // 将空参构造器的Accessible设置为true declareConstructor.setAccessible(true); // 调用空参构造器实例化对象 LazyMan instance1 = declareConstructor.newInstance(); LazyMan instance2 = declareConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }3.2.2 防止措施--"红绿灯策略'',设置标志位package single; import java.lang.reflect.Constructor; // 懒汉式单例 public class LazyMan { private static boolean flag = false; // 1.私有化构造函数 private LazyMan(){ synchronized (LazyMan.class){ if(flag == false){ flag = true; }else{ throw new RuntimeException("不要试图使用反射破坏单例模式"); } } } // 2.先定义单例对象但不实例化 private volatile static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ // 双重检测锁模式的懒汉式单例 DCL懒汉式 if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) throws Exception { // 获取空参构造器 Constructor<LazyMan> declareConstructor = LazyMan.class.getDeclaredConstructor(null); // 将空参构造器的Accessible设置为true declareConstructor.setAccessible(true); // 调用空参构造器实例化对象 LazyMan instance1 = declareConstructor.newInstance(); LazyMan instance2 = declareConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }3.3 V3:V2解决方案破解3.3.1 示例对字节码进行反编译获取到标志位,然后使用反射破解"红路灯"标志位package single; import java.lang.reflect.Constructor; import java.lang.reflect.Field; // 懒汉式单例 public class LazyMan { private static boolean flag = false; // 1.私有化构造函数 private LazyMan(){ synchronized (LazyMan.class){ if(flag == false){ flag = true; }else{ throw new RuntimeException("不要试图使用反射破坏单例模式"); } } } // 2.先定义单例对象但不实例化 private volatile static LazyMan lazyMan; // 3.当发起调用的时候判断对象是否实例化,如果未实例化,则实例化再返回,否则直接返回 public static LazyMan getInstance(){ // 双重检测锁模式的懒汉式单例 DCL懒汉式 if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) throws Exception { // 获取空参构造器 Constructor<LazyMan> declareConstructor = LazyMan.class.getDeclaredConstructor(null); // 将空参构造器的Accessible设置为true declareConstructor.setAccessible(true); // 调用空参构造器实例化对象 LazyMan instance1 = declareConstructor.newInstance(); // 获取"红绿灯标志位" Field flag = LazyMan.class.getDeclaredField("flag"); // 重置为"绿灯" flag.set(instance1,false); LazyMan instance2 = declareConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }single.LazyMan@4554617c single.LazyMan@74a144823.3.2 防止措施-使用枚举Enum实现单例模式import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } } public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { EnumSingle instance1 = EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class); declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at single.Test.main(EnumSingle.java:20)参考资料【狂神说Java】单例模式-23种设计模式系列单例模式Java中的双重检查锁(double checked locking) Java设计模式(一)之单例模式
2021年12月13日
625 阅读
0 评论
0 点赞
2021-12-10
设计模式学习笔记1:设计模式概述
1.什么是设计模式设计模式的概念最早是由 克⾥斯托佛·亚历⼭⼤ 在其著作 《建筑模式语⾔》 中⾸次提出的。 该书介绍了城市设计的 “语⾔”,提供了253个描述城镇、邻⾥、住宅、花园、房间及⻄部构造的模式, ⽽此类“语⾔” 的基本单元就是模式。后来, 埃⾥希·伽玛 、 约翰·弗利赛德斯 、 拉尔夫·约翰逊 和 理查德·赫尔姆 这四位大佬即GoF(Gang of Four,四人组/四人帮)接受了模式的概念。 1995 年, 他们出版了 《设计模式: 可复⽤⾯向对象软件的基础》⼀书,将设计模式的概念应⽤到程序开发领域中。该书 共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称GoF设计模式。简单来说,设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。2.学习设计模式的意义设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。正确使用设计模式具有以下优点:可以提高程序员的思维能力、编程能力和设计能力。使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。3.设计模式的基本要素4.GoF 23创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。结构型模式:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式行为型模式:模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式。5.OOP七大原则开闭原则:对扩展开放,对修改关闭里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立依赖倒置原则:要面向接口编程,不要面向实现编程。单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。接口隔离原则:要为各个类建立它们需要的专用接口。迪米特法则:只与你的直接朋友交谈,不跟"陌生人"说话。合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。参考资料【狂神说Java】通俗易懂的23种设计模式教学(停更)设计模式简介
2021年12月10日
540 阅读
0 评论
0 点赞
1
2
3