Java 设计模式系列:享元模式

Java设计模式结构型模式(11)--- 享元模式 *** @Description: 抽象享元角色//获取图形的方法 public abstract String getShape();//显示图形及颜色 public void display(String color) {System . out . println("方块形状:" + getShape() + ", 颜色:" + color);} } 阅读详情

简介

享元模式(Flyweight Pattern)是一种软件设计模式,用于减少内存使用和提高性能。它通过共享细粒度对象来减少创建和销毁对象时所需的内存。享元模式适用于大量相似对象的场景,这些对象可以共享相同的状态和行为。

享元模式的核心思想是将对象分为内部状态和外部状态。内部状态是对象自身的状态,通常是不可变的,可以共享;而外部状态则是与对象相关联的环境信息,通常是变化的,由客户端传入享元对象内部。通过共享内部状态,可以显著减少系统中的对象数量,从而降低内存消耗。

结构

享元模式包含以下几个角色:

  1. 抽象享元类(Flyweight):通常是一个接口或抽象类,声明一些可以向外界提供享元对象内部状态的方法,同时也可以通过这些方法设置内部状态。
  2. 具体享元类(ConcreteFlyweight):实现了抽象享元类,为内部状态提供了存储空间。
  3. 非共享具体享元类(UnsharedConcreteFlyweight):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可以设计为非共享具体享元类,当需要使用非共享具体享元类时可以直接实例化创建。
  4. 享元工厂类(FlyweightFactory):享元工厂类用于创建并管理享元对象,将各个类型的具体享元对象存储在一个享元池中,享元池一般设计为键值对集合结构。

标准的享元模式结构包含可以共享的具体享元类和不可以共享的非共享具体享元类。根据具体情况,可以分为单纯享元模式和复合享元模式。单纯享元模式中所有具体享元类都是可以共享的,而复合享元模式则将一些单纯享元对象使用组合模式加以组合,形成复合享元对象。

案例实现

例】俄罗斯方块

下面的图片是众所周知的俄罗斯方块中的一个个方块,如果在俄罗斯方块这个游戏中,每个不同的方块都是一个实例对象,这些对象就要占用很多的内存空间,下面利用享元模式进行实现。

在这里插入图片描述

先来看类图:

在这里插入图片描述

代码如下:

俄罗斯方块有不同的形状,我们可以对这些形状向上抽取出AbstractBox,用来定义共性的属性和行为。

public abstract class AbstractBox {
    public abstract String getShape();

    public void display(String color) {
        System.out.println("方块形状:" + this.getShape() + " 颜色:" + color);
    }
}

接下来就是定义不同的形状了,IBox类、LBox类、OBox类等。

public class IBox extends AbstractBox {

    @Override
    public String getShape() {
        return "I";
    }
}

public class LBox extends AbstractBox {

    @Override
    public String getShape() {
        return "L";
    }
}

public class OBox extends AbstractBox {

    @Override
    public String getShape() {
        return "O";
    }
}

提供了一个工厂类(BoxFactory),用来管理享元对象(也就是AbstractBox子类对象),该工厂类对象只需要一个,所以可以使用单例模式。并给工厂类提供一个获取形状的方法。

public class BoxFactory {

    private static HashMap<String, AbstractBox> map;

    private BoxFactory() {
        map = new HashMap<String, AbstractBox>();
        AbstractBox iBox = new IBox();
        AbstractBox lBox = new LBox();
        AbstractBox oBox = new OBox();
        map.put("I", iBox);
        map.put("L", lBox);
        map.put("O", oBox);
    }

    public static final BoxFactory getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final BoxFactory INSTANCE = new BoxFactory();
    }

    public AbstractBox getBox(String key) {
        return map.get(key);
    }
}

优缺点和使用场景

1,优点:

  1. 降低内存消耗:通过共享内部状态,可以显著减少系统中的对象数量,从而降低内存消耗。
  2. 提高性能:由于减少了对象的创建和销毁次数,可以提高系统的性能。
  3. 提高复用性:通过将对象的状态分离出来,可以使多个对象共享相同的状态,提高了代码的复用性。

2,缺点:

  1. 实现复杂度较高:需要分离出内部状态和外部状态,使得程序逻辑复杂化。
  2. 需要分离出共享状态和非共享状态:为了实现对象的共享,需要将对象的共享状态和非共享状态进行分离,这可能会增加代码的复杂度。

3,使用场景:

  • 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
  • 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

源码中的应用

Spring中

在Spring框架中,享元模式的应用主要体现在以下几个方面:

  1. Bean的作用域管理:Spring框架中的Bean默认是单例的,这意味着在整个Spring容器中,每个Bean的ID都对应着一个唯一的实例对象。这种单例模式实际上是享元模式的一种应用。通过共享相同的Bean实例,减少了对象的创建和销毁,从而节约了系统资源,提高了应用程序的性能。
  2. 事件处理机制:在Spring的事件处理机制中,享元模式被用来管理事件监听器对象的创建和销毁。通过共享已经存在的监听器对象,避免了大量相似对象的创建,从而节省了系统资源,提高了事件处理的效率。
  3. BeanFactory管理:在Spring的BeanFactory中,也使用了享元模式来管理Bean对象的创建和销毁。BeanFactory负责实例化、配置和管理Bean,通过共享Bean的实例,实现了对象的复用,进一步提高了系统资源的利用率。
  4. 缓存机制:在Spring Security等模块中,权限信息等数据经常被缓存以提高性能。这些缓存的实例在需要时被创建,并在多个上下文中共享,这也是享元模式的一种应用。通过共享相同的权限对象实例,减少了对象的创建和内存占用,提高了系统的响应速度。

JDK中

享元模式在JDK中的应用主要体现在一些系统库和组件的设计中,旨在通过共享对象来减少内存使用和提高性能。下面是一些具体的例子:

  1. 字符串常量池:在JDK中,字符串常量是通过享元模式来管理的。当我们使用双引号创建字符串字面量时,JVM会首先检查字符串常量池中是否存在相同的字符串。如果存在,则返回该字符串的引用;如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。这种机制减少了相似字符串对象的创建,节省了内存空间。

  2. 包装类缓存:Java中的包装类(如Integer、Boolean等)对于小范围的数值,如Integer在-128到127之间,使用了享元模式。这些范围内的对象会被缓存起来,当需要创建这些范围内的对象时,会直接从缓存中获取,而不是重新创建。这种机制减少了包装类对象的创建,提高了性能。

    例:java.lang.Integer

    https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Integer.java

    public class Demo {
        public static void main(String[] args) {
            Integer i1 = 127;
            Integer i2 = 127;
    
            System.out.println("i1和i2对象是否是同一个对象?" + (i1 == i2));
    
            Integer i3 = 128;
            Integer i4 = 128;
    
            System.out.println("i3和i4对象是否是同一个对象?" + (i3 == i4));
        }
    }
    

    运行上面代码,结果如下:

在这里插入图片描述

为什么第一个输出语句输出的是true,第二个输出语句输出的是false?通过反编译软件进行反编译,代码如下:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf((int)127);
        Integer i2 Integer.valueOf((int)127);
        System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
        Integer i3 = Integer.valueOf((int)128);
        Integer i4 = Integer.valueOf((int)128);
        System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());
    }
}

上面代码可以看到,直接给Integer类型的变量赋值基本数据类型数据的操作底层使用的是 valueOf() ,所以只需要看该方法即可

public final class Integer extends Number implements Comparable<Integer> {
    
	public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

可以看到 Integer 默认先创建并缓存 -128 ~ 127 之间数的 Integer 对象,当调用 valueOf 时如果参数在 -128 ~ 127 之间则计算下标并从缓存中返回,否则创建一个新的 Integer 对象。

  1. 集合框架中的对象复用:在JDK的集合框架中,有些实现也采用了享元模式的思想。例如,在HashMap等集合类中,为了优化性能,可能会复用某些内部对象,而不是每次都创建新的对象。

  2. 线程池:JDK中的线程池(如ExecutorService)也是享元模式的一种应用。线程池通过复用已有的线程,减少了线程的创建和销毁开销,提高了系统的并发处理能力。

设计模式-享元模式 前面我们接收了设计模式中的外观模式,今天我们来介绍一下享元模式。 先看个视频了解一下 我们还是抛出一个实际的案例,帮助大家理解: 一.需求: 小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希 望做这样的产品展示网站,但是要求都有些同: 1) 有客户要求以新闻的形式发布 2) 有客户人要求以博客的形式发布 3) 有客户希望以微信公众号的形式发布 传统方案解决网站展现项目 1) 直接复制粘贴一份,然后根据客户不同要求,进行定制修改 2) 给每个网站租用 阅读详情

相关推荐

【重温设计模式】享元模式及其Java示例

在编程世界中,我们常常面临着如何有效管理系统资源的挑战。这就好比我们在生活中,面对有限的物质资源,如何做到既满足需求又节约使用,是一门艺术。在设计模式中,有一种模式,恰如其分地解决了这个问题,那就是享元模式。享元模式,源自英文Flyweight Pattern,是一种用于性能优化的模式,其核心思想是共享对象,以减少系统中对象的数量,从而减少系统内存的使用,提高系统的性能。这种模式的名称来源于国际象棋中的棋子,棋子虽然有多个,但其本质上的属性都是相同的,只是位置不同,这就是享元模式的精髓。

万猫学社 1290

设计模式第14讲——享元模式(Flyweight)

享元模式是一种结构型的设计模式。一、什么是享元模式 二、 角色组成 三、优缺点 四、应用场景 4.1 生活场景 4.2 java场景 五、代码实现 5.0 代码结构5.1 Bike——抽象享元类(FlyWeight)5.2 具体享元类(ConcreteFlyWeight)5.3 BikeFactory——享元工厂(FlyWeightFactory)5.4 testFlyWeight 六、总结

让~学习~成为一种习惯(橡 皮 人の技术博客) 1万+

汽车电缆选型实战:基于ISO 19642-3~12的7类电缆电压/材料/场景对照表

本文详细解析了基于ISO 19642-3~12标准的7类汽车电缆选型实战,涵盖电压等级、导体材料和场景应用的对照表。通过全参数速查表和典型选型陷阱分析,帮助工程师快速掌握从低压信号线到高压动力电缆的选型逻辑,提升新能源汽车与智能驾驶技术的安全性和可靠性。

weixin_29043895的博客 364

享元模式(Flyweight)

享元模式(Flyweight Pattern)是一种结构型设计模式,它主要解决的问题是创建大量相似对象时的内存开销问题。该模式通过共享具有相同状态的对象来减少内存使用量。享元模式的思想是:当需要创建一个新对象时,首先检查是否已经存在具有相同状态的对象。如果存在,则返回已经存在的对象,否则创建一个新的对象。因此,如果要创建多个具有相同状态的对象,可以重复使用相同的对象,从而减少内存开销。通过运用共享技术来有效地支持大量细粒度对象的复用。

qq_53868937的博客 4260

QGroundControl或者华科尔地面站安装完打不开问题解决

QGroundControl 华科尔 打不开

wv112406的博客 4886

忘记安卓锁屏密码怎么办?3种方法教你用ADB恢复访问(含gesture.key解密)

本文详细解析了安卓设备锁屏密码的存储与加密机制,特别是gesture.key文件的解密原理。针对遗忘密码的常见问题,提供了三种核心恢复方法:利用ADB调试接口删除密码文件、对gesture.key进行离线分析与解密,以及应对高版本系统的替代方案。重点介绍了通过ADB工具在Recovery模式下操作的具体步骤,帮助用户在合法合规前提下恢复设备访问权限。

php55的博客 955

千锋教育威哥学Java——JAVA教程系列:Java设计模式之享元模式

享元模式有点类似于单例模式,都是只生成一个对象被共享使用。享元模式主要目的就是让多个对象实现共享,减少不必要的内存消耗,将多对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。👆👆👆【各学科****因为享元模式结构比较复杂,一般结合工厂模式一起使用,在它的结构图中包含了一个享元工厂类。

2401_84240129的博客 984

ROS2节点通信实战:用C++手写一个‘智能小车’控制模型(附完整代码)

本文详细介绍了如何使用ROS2和C++构建一个智能小车控制模型,涵盖运动控制节点、电池管理服务、参数动态配置和Rviz可视化等核心功能。通过实战项目演示ROS2节点通信机制,帮助开发者快速掌握机器人开发的关键技术,并提供了完整的代码实现和系统集成方案。

weixin_30607029的博客 260

千锋教育威哥学Java——JAVA教程系列:Java设计模式之享元模式(1)

👆👆👆【各学科****因为享元模式结构比较复杂,一般结合工厂模式一起使用,在它的结构图中包含了一个享元工厂类。享元模式中引入了享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,当用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。接下来,实现一个登陆的享元模式。还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!

2401_84240129的博客 856

【一键搞定】ThinkPHP6.0.2+PHPStudy+Composer极速安装指南——附完整下载包与避坑技巧

本文提供了一份ThinkPHP6.0.2的完整下载包,并详细介绍了如何配合PHPStudy和Composer进行极速安装与配置。指南涵盖了环境准备、Composer国内镜像加速、项目部署及常见避坑技巧,旨在帮助开发者,尤其是内网或网络不佳的用户,快速搭建可运行的TP6开发环境,绕过依赖下载的繁琐步骤。

assembly8low的博客 1005

告别盲调!用Synopsys VIP搭建PCIe 5.0验证环境,手把手搞定链路训练与均衡调试

本文详细介绍了基于Synopsys VIP搭建PCIe 5.0验证环境的实战指南,重点解析了链路训练(LTSSM)与均衡调试的关键技术。通过配置VIP组件、设计验证平台架构及实战调试技巧,帮助工程师高效解决PCIe 5.0验证中的常见问题,提升芯片验证效率与可靠性。

weixin_28700411的博客 456

千锋教育威哥学Java——JAVA教程系列:Java设计模式之享元模式(2)

享元模式有点类似于单例模式,都是只生成一个对象被共享使用。享元模式主要目的就是让多个对象实现共享,减少不必要的内存消耗,将多对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。👆👆👆【各学科****因为享元模式结构比较复杂,一般结合工厂模式一起使用,在它的结构图中包含了一个享元工厂类。还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!

2401_84240129的博客 545

强化学习优化多目标冲突推理的决策过程设计

1. 背景介绍 强化学习(Reinforcement Learning,RL)作为机器学习的一个重要分支,近年来在计算机科学和人工智能领域取得了显著的进展。其核心思想是通过试错来学习最优策略,从而在复杂的环境中实现目标。然而,在多目标冲突推理(Multi-Objective Conflict Reasoning,MOCR)问题上,强化学习面临着诸多挑

AI天才研究院 1509

java设计模式

1、单例模式的特点 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、从系统启动到终止,整个过程只会产生一个实例。 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 主要解决:一个全局使用的类频繁地创建与销毁。 何时使用:当您想控制实例数目,节省系统资源的时候。 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。 关键代码:构造函数是私有的。 2、单例模式两种实现方式 ①、饿汉式: 在程序启动或单件模式类被加载的时候,单

asddasddeedd的博客 336

YOLO项目从零开始:超详细运行教程(图文保姆级教学)

至此,我们已经完整跑通了YOLO项目的安装与启动流程,无论你是零基础小白还是刚入门的AI新兵,只要按部就班操作,也能轻松实现目标检测项目的落地。

一键难忘的博客 4797

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【享元模式】设计模式系列:深入探索享元模式(优化内存管理的艺术)

本篇文章详尽地探讨了享元模式在Java中的应用,从理论基础到实战案例,逐步引导读者掌握如何利用此模式有效减少内存占用并提升性能。通过深入浅出的解释、丰富的示例代码以及实用的最佳实践指南,您将学会何时及如何在自己的项目中运用享元模式。

欢迎来到我的技术空间!我是一名经验丰富的Java开发工程师,热衷于分享我在软件开发领域的知识和心得。在这个博客里,您会发现一系列关于Java编程的文章,包括最佳实践、技术趋势、代码优化技巧以及一些有趣的项目案例。无论是初学者还是有经验的开发者,都能在这里找 1795

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

瑞_23种设计模式_享元模式

本文章为瑞_系列专栏之《23种设计模式》的享元模式篇. 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。JDK中Integer类使用了享元模式。

阿瑞——从事Java后端,喜欢研读源码、自研框架、分享经验 2162

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

常用设计模式系列之——享元模式

每天早上七点三十,准时推送干货Photo byJoshua EarleonUnsplashHello 大家好,我是鸭血粉丝,今天周六,你是在休息学习还是在加班呢?今天阿粉要跟大家分...

Java极客技术 911

【设计模式系列】享元模式(十五)

享元模式核心思想是使用共享的方式来高效地管理大量细粒度的对象。

LHW0621的专栏 1498

【设计模式系列13】今天才知道,原来我一直在用享元模式

享元模式分析前言什么是享元模式享元模式示例享元模式状态内部状态外部状态享元模式示例2享元模式角色享元模式在JDK源码中的体现享元模式应用场景享元模式优缺点优点缺点总结 前言 我们知道,数据库的连接非常消耗性能,所以就有了连接池来减少连接操作的性能消耗; 如果一个系统中需要创建大量线程,也会消耗大量性能,所以就有了线程池。而我们在面向对象过程中,会创建大量对象,而如果有些对象可以被重复使用,那么我们是不是可以创建一个对象池来减少创建对象所带来的性能消耗呢? 答案是肯定的,这就是我们今天需要学习的享元模式做的事

zwx900102的博客 1779

23种设计模式之享元模式(Flyweight Pattern)

本篇文章将23种设计模式中的享元模式,此篇文章为一天学习一个设计模式系列文章,后面会分享其他模式知识。

小威的博客 2万+

Java设计模式之享元模式

Java设计模式之享元模式前言正文一、享元池二、享元的内部状态和外部状态三、定义四、结构五、实现六、单纯享元模式与复合享元模式1、单纯享元模式2、复合享元模式七、享元模式与String类八、享元模式优缺点与适用环境优点缺点适用环境 前言 当系统中存在大量相同或者相似的对象时,享元模式是一种值得考虑的解决方案,它通过 共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间、提高了系统性能。在享...

js_tengzi的博客 516

【设计模式】享元模式(轻量级模式) 单纯享元模式和复合享元模式

享元模式是一种结构型设计模式,通过共享技术实现相同或相似对象的重用,以减少内存占用和提高性能。它将对象分为内部状态(可共享)和外部状态(不可共享),通过享元池管理共享对象。典型应用场景包括字符串池、缓存管理、游戏开发等。该模式通过分离对象内部和外部状态,让多个对象共享相同的内部状态,减少对象创建数量。代码示例展示了如何用享元模式优化文本编辑器字体样式管理,通过工厂类缓存和复用相同样式的字符对象,验证了共享实例的实现效果。

weixin_44131922的博客 1044

《JAVA设计模式系列》享元模式

它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。FlyWeight:抽象的享元角色,产品的抽象类,定义了对象的外部状态和内部状态的接口或实现。...........

追寻心的步伐 470
上一篇: Asp .Net Core 系列:集成 Refit 和 RestEase 声明式 HTTP 客户端库
下一篇: Java 设计模式系列:模板方法模式
Code技术分享
博客等级 码龄7年 3327粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值