博客
关于我
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/

你可能感兴趣的文章
nodejs + socket.io 同时使用http 和 https
查看>>
NodeJS @kubernetes/client-node连接到kubernetes集群的方法
查看>>
Nodejs express 获取url参数,post参数的三种方式
查看>>
nodejs http小爬虫
查看>>
nodejs libararies
查看>>
nodejs npm常用命令
查看>>
nodejs npm常用命令
查看>>
Nodejs process.nextTick() 使用详解
查看>>
nodejs 创建HTTP服务器详解
查看>>
nodejs 发起 GET 请求示例和 POST 请求示例
查看>>
NodeJS 导入导出模块的方法( 代码演示 )
查看>>
nodejs 开发websocket 笔记
查看>>
nodejs 的 Buffer 详解
查看>>
nodejs 读取xlsx文件内容
查看>>
nodejs 运行CMD命令
查看>>
Nodejs+Express+Mysql实现简单用户管理增删改查
查看>>
nodejs+nginx获取真实ip
查看>>
nodejs-mime类型
查看>>
NodeJs——(11)控制权转移next
查看>>
NodeJS、NPM安装配置步骤(windows版本)
查看>>