博客
关于我
Java中的单例模式
阅读量:580 次
发布时间:2019-03-11

本文共 3028 字,大约阅读时间需要 10 分钟。

单例模式:饿汉式与懒汉式的比较

单例模式是一种常用的设计模式,用于确保一个类在程序运行期间只创建一个实例。在实际开发中,单例模式广泛应用于资源管理、缓存机制等场景。以下将从饿汉式和DCL懒汉式两种实现方式入手,详细分析其优缺点。

饿汉式单例模式

饿汉式单例模式的特点是“先创建再用”,其实现方式非常直接且简单。以下是典型代码示例:

package com.chao.single;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;    }}

优点:

  • 实现简单,易于理解。
  • 内存中只保留一个实例,节省内存空间。
  • 适用于对内存占用要求较高的场景。

缺点:

  • 在对象初始化时就分配内存,可能导致内存浪费。
  • 如果对象无法被销毁,可能导致内存泄漏。

DCL懒汉式单例模式

DCL懒汉式单例模式的特点是“先用后创建”,通过双重锁机制确保单例的初始化。其实现方式如下:

package com.chao.single;import java.lang.reflect.Constructor;import java.lang.reflect.Field;public class LazyMan {    private static boolean chao = false;    private LazyMan() {        synchronized (LazyMan.class) {            if (chao == false) {                chao = true;            } else {                throw new RuntimeException("不要试图使用反射破坏异常");            }        }    }        private volatile static LazyMan lazyMan;        public static LazyMan getInstance() {        if (lazyMan == null) {            synchronized (LazyMan.class) {                if (lazyMan == null) {                    lazyMan = new LazyMan();                }            }        }        return lazyMan;    }}

优点:

  • 内存占用较低,延迟加载机制减少内存预先分配。
  • 适用于内存资源有限或对象创建频繁的场景。

缺点:

  • 在高并发场景下可能导致性能问题。
  • 双重锁机制增加了额外的同步开销。
  • 单例对象在某些情况下可能无法被销毁,导致内存泄漏。

静态内部类单例模式

静态内部类单例模式通过静态嵌套类实现单例模式,代码结构清晰且易于理解。其实现方式如下:

package com.chao.single;public class Holder {    private Holder() {    }        public static Holder getInstance() {        return InnerClass.HOLDER;    }        public static class InnerClass {        private static final Holder HOLDER = new Holder();    }}

优点:

  • 内部类与外部类的关系清晰,逻辑分离明确。
  • 利用Java的静态嵌套机制,实现了单例模式。
  • 提供了较高的内存安全性。

缺点:

  • 类结构较为复杂,初次理解可能需要时间。
  • 在某些反射攻击下可能存在安全隐患。

单例模式的反射攻击

反射攻击是对单例模式的一种常见测试,验证单例模式的健壮性。以下是一个反射攻击的示例:

public class Test {    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {        EnumSingle instance1 = EnumSingle.INSTANCE;        Constructor declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);        declaredConstructor.setAccessible(true);        EnumSingle instance2 = declaredConstructor.newInstance();        System.out.println(instance1);        System.out.println(instance2);    }}

防御措施:

  • 使用私有构造器并设置Accessible属性。
  • 在构造器中添加异常处理,防止反射攻击。
  • 在单例类中添加反射保护机制。

枚举单例模式

枚举类型在Java中本身就是单例的概念。通过枚举来实现单例模式,其优雅之处在于无需手动管理单例实例。以下是一个枚举单例的实现示例:

package com.chao.single;import java.lang.reflect.Constructor;public enum EnumSingle {    INSTANCE;        public EnumSingle getInstance() {        return INSTANCE;    }}

优点:

  • 枚举类型天然支持单例模式。
  • 编译后不可变,提高运行时安全性。
  • 易于管理和理解。

缺点:

  • 枚举常量无法改变,灵活性较低。
  • 在反射攻击下可能存在安全隐患。

总结

单例模式通过控制对象的创建次数,保障了内存资源的高效利用。在实际开发中,选择合适的单例模式需要根据具体需求进行权衡。饿汉式和懒汉式各有优劣,静态内部类和枚举方式则提供了不同的实现思路。无论选择哪种方式,理解其内部机制和潜在问题都是至关重要的。

转载地址:http://igrvz.baihongyu.com/

你可能感兴趣的文章
PostgreSQL 服务启动后停止
查看>>
PostgreSQL 辟谣存在任意代码执行漏洞:消息不实
查看>>
PostgreSQL+PostGIS实现两坐标点之间最短路径查询算法函数(地图工具篇.12)
查看>>
Qt开发——简易调色板QPalette
查看>>
PostgreSQL-解决连接时遇到的乱码问题
查看>>
PostgreSQL15.2最新版本安装_远程连接_Navicat操作_pgAdmin操作_Windows10上安装---PostgreSQL工作笔记001
查看>>
PostgreSQL9.1 双机部署配置(主备数据同步)
查看>>
Qt开发——简易网络浏览器(一)
查看>>
Qt开发——简易成绩登记系统
查看>>
Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句
查看>>
Postgresql中PL/pgSQL的游标、自定义函数、存储过程的使用
查看>>
SpringBoot中集成XXL-JOB分布式任务调度平台,轻量级、低侵入实现定时任务
查看>>
Postgresql中的表结构和数据同步/数据传输到Mysql
查看>>
Postgresql中自增主键序列的使用以及数据传输时提示:错误:关系“xxx_xx_xx_seq“不存在
查看>>
SpringBoot中集成websocket后WebSocketServer中注入mapper为空
查看>>
postgreSQL入门命令
查看>>
PostgreSQL删除数据库报"ERROR: There is 1 other session using the database."
查看>>
Qt开发——爱情公寓人事管理系统
查看>>
Qt开发——文件下载软件
查看>>
PostgreSQL和Oracle两种数据库有啥区别?如何选择?
查看>>