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

你可能感兴趣的文章
Netty原理分析及实战(四)-客户端与服务端双向通信
查看>>
Netty客户端断线重连实现及问题思考
查看>>
Netty工作笔记0006---NIO的Buffer说明
查看>>
Netty工作笔记0007---NIO的三大核心组件关系
查看>>
Netty工作笔记0011---Channel应用案例2
查看>>
Netty工作笔记0013---Channel应用案例4Copy图片
查看>>
Netty工作笔记0014---Buffer类型化和只读
查看>>
Netty工作笔记0020---Selectionkey在NIO体系
查看>>
Vue踩坑笔记 - 关于vue静态资源引入的问题
查看>>
Netty工作笔记0025---SocketChannel API
查看>>
Netty工作笔记0027---NIO 网络编程应用--群聊系统2--服务器编写2
查看>>
Netty工作笔记0050---Netty核心模块1
查看>>
Netty工作笔记0057---Netty群聊系统服务端
查看>>
Netty工作笔记0060---Tcp长连接和短连接_Http长连接和短连接_UDP长连接和短连接
查看>>
Netty工作笔记0063---WebSocket长连接开发2
查看>>
Netty工作笔记0070---Protobuf使用案例Codec使用
查看>>
Netty工作笔记0077---handler链调用机制实例4
查看>>
Netty工作笔记0084---通过自定义协议解决粘包拆包问题2
查看>>
Netty工作笔记0085---TCP粘包拆包内容梳理
查看>>
Netty常用组件一
查看>>