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

你可能感兴趣的文章
Node中的Http模块和Url模块的使用
查看>>
Node中自启动工具supervisor的使用
查看>>
Node入门之创建第一个HelloNode
查看>>
node全局对象 文件系统
查看>>
Node出错导致运行崩溃的解决方案
查看>>
Node响应中文时解决乱码问题
查看>>
node基础(二)_模块以及处理乱码问题
查看>>
node安装卸载linux,Linux运维知识之linux 卸载安装node npm
查看>>
node安装及配置之windows版
查看>>
Node实现小爬虫
查看>>
Node提示:error code Z_BUF_ERROR,error error -5,error zlib:unexpected end of file
查看>>
Node提示:npm does not support Node.js v12.16.3
查看>>
Node搭建静态资源服务器时后缀名与响应头映射关系的Json文件
查看>>
Node服务在断开SSH后停止运行解决方案(创建守护进程)
查看>>
node模块化
查看>>
node模块的本质
查看>>
node环境下使用import引入外部文件出错
查看>>
node环境:Error listen EADDRINUSE :::3000
查看>>
Node的Web应用框架Express的简介与搭建HelloWorld
查看>>
Node第一天
查看>>