当前位置: 首页 > news >正文

用私有构造器或者枚举类型强化Singleton 属性

用私有构造器或者枚举类型强化Singleton 属性

    • 1. Singleton 的含义
      • 1.1 懒汉模式
      • 1.2 饿汉模式
      • 1.3 序列化
      • 1.4 总结
    • 2 通过反射获得不同实例
      • 2.1 举例说明
      • 2.2 通过异常来防止二次实例对象
      • 2.3 总结
    • 3. 枚举类型

1. Singleton 的含义

Singleton 大部分人都知道是单例的意思,因为它一般都是我们理解设计模式时,最好理解的一个。它的作用就是 使这个类就被实例化一次。在JDK1.5 前实现有两种方式,分别是 懒汉模式饿汉模式

1.1 懒汉模式

  1. 1/** 
  2. 2 * 懒汉模式 
  3. 3 */ 
  4. 4public class Sengleton_Lyze
  5. 5 //1,构造方法私有化 
  6. 6 private Sengleton_Lyze(){  
  7. 7
  8. 8  
  9. 9 //2,创建类的位唯一实例,private static 修饰 
  10. 10 private static Sengleton_Lyze instance; 
  11. 11  
  12. 12 //3,提供获取实例的方法,public static 修饰 
  13. 13 public static Sengleton_Lyze getInstance()
  14. 14 if (instance==null) { 
  15. 15 instance=new Sengleton_Lyze(); 
  16. 16
  17. 17 return instance; 
  18. 18
  19. 19

为什么使用getInstance()方法来获得实例?

  • 它可以被很灵活的修改,用户在不需要修改API的情况下,我们可以改变这个类是否是单例的。

  • 第二个优势在泛型

1.2 饿汉模式

  1. 1/** 
  2. 2 * 单例模式Sengleton 
  3. 3 * 试用实际场合:有些对象只需要一个就够了。 
  4. 4 * 作用:保证整个实际应用程序中某个实例有且只有一个 
  5. 5 * 类别:饿汉模式,懒汉模式 
  6. 6 * 区别:饿汉模式的特点,加载类时比较慢,获取比较慢。线程安全的。 
  7. 7 *  
  8. 8 *  
  9. 9 */ 
  10. 10public class Sengleton_Hunger
  11. 11 //1,构造方法私有化,不允许外接直接创建对象 
  12. 12 private Sengleton_Hunger(){  
  13. 13
  14. 14 //2,创建类的唯一实例,使用private static 修饰 
  15. 15 private static Sengleton_Hunger instance = new Sengleton_Hunger(); 
  16. 16  
  17. 17 //3,获取这个实例的方法 
  18. 18 public static Sengleton_Hunger getInstance()
  19. 19 return instance; 
  20. 20
  21. 21  
  22. 22

运行代码

  1. 1public class Test
  2. 2 public static void main(String[] args)
  3. 3  
  4. 4 //饿汉模式 
  5. 5 Sengleton_Hunger s1=Sengleton_Hunger.getInstance(); 
  6. 6 Sengleton_Hunger s2=Sengleton_Hunger.getInstance(); 
  7. 7 if (s1==s2) { 
  8. 8 System.out.println("true"); 
  9. 9 }else
  10. 10 System.out.println("false"); 
  11. 11
  12. 12  
  13. 13 //懒汉模式 
  14. 14 Sengleton_Lyze s3=Sengleton_Lyze.getInstance(); 
  15. 15 Sengleton_Lyze s4=Sengleton_Lyze.getInstance(); 
  16. 16 if (s1==s2) { 
  17. 17 System.out.println("一样的"); 
  18. 18 }else
  19. 19 System.out.println("不同的"); 
  20. 20
  21. 21
  22. 22 
  23. 23

运行结果

true
一样的

1.3 序列化

为了让成为单例的类实现 序列化的(Serializable),我们仅仅在声明上加上 "implements Serializable" 是不够的。 为了维护并保证 单例,必须要声明所有实例域都是 瞬时(transient) 的,并提供一个readResolve方法。否则,每次反序列化一个序列化的实例时,都会创建一个新的实例。如,我们可以为我们的 懒汉模式 加上这个方法

  1. 1 private Object readResolve()
  2. 2 return instance; 
  3. 3

1.4 总结

  • 都需要构造方法私有化

  • 提供公有的静态成员,方便客户端获取类的唯一实例

  • 序列化时需要提供额外的readResolve 方法,防止反序列化时创建对象
    总体上来说,懒汉就是时间换空间,在需要获取的时候才会去实例化。而饿汉就是空间换时间,不管使用否,都会实例化一个放入内存中。饿汉模式的特点,加载类时比较慢,获取比较慢、线程安全的,饿汉反之。
    但是如果客户端拥有 AccessibleObject.setAccessible() 的权限,就可以通过反射机制调用私有构造器

2 通过反射获得不同实例

2.1 举例说明

  1. 1public class Test
  2. 2 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
  3. 3 //饿汉模式 
  4. 4 Constructor sengleton_Hunger = Sengleton_Hunger.class.getDeclaredConstructor(); 
  5. 5 sengleton_Hunger.setAccessible(true); 
  6. 6 Sengleton_Hunger s1= (Sengleton_Hunger) sengleton_Hunger.newInstance(); 
  7. 7 Sengleton_Hunger s2= (Sengleton_Hunger) sengleton_Hunger.newInstance(); 
  8. 8 if (s1==s2) { 
  9. 9 System.out.println("true"); 
  10. 10 }else
  11. 11 System.out.println("false"); 
  12. 12
  13. 13  
  14. 14 //懒汉模式 
  15. 15 Constructor sengleton_Lyze = Sengleton_Lyze.class.getDeclaredConstructor(); 
  16. 16 sengleton_Lyze.setAccessible(true); 
  17. 17 Sengleton_Lyze s3= (Sengleton_Lyze) sengleton_Lyze.newInstance(); 
  18. 18 Sengleton_Lyze s4= (Sengleton_Lyze) sengleton_Lyze.newInstance(); 
  19. 19 if (s1==s2) { 
  20. 20 System.out.println("一样的"); 
  21. 21 }else
  22. 22 System.out.println("不同的"); 
  23. 23
  24. 24
  25. 25 
  26. 26

显示的结果

false
不同的

2.2 通过异常来防止二次实例对象

public class Sltn {
    private static Sltn s = null;
    private static boolean flag = true;
    
    private Sltn(){
        System.out.println("flag:" + flag);
        if(flag){  
            flag = !flag;  
        }else{  
            try {  
                throw new Exception("duplicate instance create error!" + Sltn.class.getName());  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }
    
    public static Sltn getInstance(){
        if(null == s){  
            s = new Sltn();  
        }  
        return s;  
    }
}

使用反射第二次创建就会抛出异常

2.3 总结

  • 通过反射在一定的条件下,是可以使用 类私有的构造方法来获得不同对象的。

  • 通过在类私有构造方法中加入条件,可以防止反射获得第二个实例,但写法有些多余、复杂。

3. 枚举类型

在JDK1.5之后出现了第三种实现单例模式的方式 ,只需要编写一个含有单个元素的枚举类型

public enum Elvis {
    INSTANCE;
    public void leaveTheBuilder(){
    }
}
  • 这种方法更加的简洁

  • 无偿提供序列化机制,绝对防止多次实例化

  • 可以面对复杂的序列化或者反射攻击

  • 单元素的枚举类型已经成为了实现Singleton(单例)的最佳方法

转载于:https://www.cnblogs.com/ybbzbb/p/5524261.html

相关文章:

  • 阿里分布式事务框架GTS(Seata)开源啦!
  • 完了!生产事故!几百万消息在消息队列里积压了几个小时!
  • [原创]java WEB学习笔记18:java EE 中的MVC 设计模式(理论)
  • 阿里巴巴的26款超神Java开源项目!
  • window下使用vnc远程登录阿里云ECS/ubuntu图形界面
  • 看似简单的hashCode和equals面试题,竟然有这么多坑!
  • 2019年互联网高频Java面试题指南!互联网升职加薪方案!
  • FZU 2112 并查集、欧拉通路
  • 看了这篇Dubbo RPC面试题,让天下没有难面的面试题!
  • JavaScript的面向对象编程(OOP)(二)——原型
  • Alibaba之Nacos详解
  • 10款常见MySQL高可用方案选型解读
  • wireshark抓包图解 TCP三次握手/四次挥手详解
  • Redis实战和核心原理详解(1)Centos7.0下安装Redis 5.0详细过程和使用常见问题
  • 自定义上传按钮 input type=file name = file/ (将file隐藏在button下)
  • (三)从jvm层面了解线程的启动和停止
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • css系列之关于字体的事
  • css属性的继承、初识值、计算值、当前值、应用值
  • JavaScript 奇技淫巧
  • js ES6 求数组的交集,并集,还有差集
  • Koa2 之文件上传下载
  • mockjs让前端开发独立于后端
  • ng6--错误信息小结(持续更新)
  • Octave 入门
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • VUE es6技巧写法(持续更新中~~~)
  • 初识 beanstalkd
  • 聊聊hikari连接池的leakDetectionThreshold
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 新书推荐|Windows黑客编程技术详解
  • 学习笔记TF060:图像语音结合,看图说话
  • 用element的upload组件实现多图片上传和压缩
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • #vue3 实现前端下载excel文件模板功能
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (Ruby)Ubuntu12.04安装Rails环境
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (数据结构)顺序表的定义
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (转)shell调试方法
  • .gitattributes 文件
  • .NET Core WebAPI中封装Swagger配置
  • .net core 连接数据库,通过数据库生成Modell
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .net6使用Sejil可视化日志
  • .net与java建立WebService再互相调用
  • @Not - Empty-Null-Blank
  • @Validated和@Valid校验参数区别