当前位置: 首页 > news >正文

Java 设计模式——享元模式

目录

  • 1.概述
  • 2.结构
  • 3.实现
    • 3.1.抽象享元
    • 3.2.具体享元
    • 3.3.享元工厂
    • 3.4.测试
  • 4.优缺点
  • 5.使用场景
  • 6.JDK 源码解析——Integer 类

1.概述

(1)享元模式 (Flyweight Pattern) 是一种结构型设计模式,主要通过共享对象来减少系统中的对象数量,从而提高系统的性能。具体来说,享元模式是通过将对象分成内部状态外部状态两种形式来实现共享:

  • 内部状态:即不会随着环境的改变而改变的可共享部分。
  • 外部状态:指随环境改变而改变的不可以共享的部分。

通过将内部状态拆分出来,并且在一个对象池中创建对象时,判断是否已经存在需要创建的对象并返回已有对象,从而避免重复创建。

(2)这种模式适用于具有大量相近对象的场景,通过对象共享的方式可以大幅度减少系统的内存开销。在实际应用中,享元模式经常与其他设计模式相结合使用,以达到更好的效果。

2.结构

享元模式的主要有以下角色:

  • 抽象享元 (Flyweight) 角色:通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
  • 具体享元 (Concrete Flyweight) 角色:它实现了抽象享元类,称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
  • 非享元 (Unsharable Flyweight) 角色:并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
  • 享元工厂 (Flyweight Factory) 角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

3.实现

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

在这里插入图片描述

类图如下:
在这里插入图片描述
具体实现代码如下:

3.1.抽象享元

AbstractBox.java

//抽象享元角色
public abstract class AbstractBox {//获取图形的方法public abstract String getShape();//显示图形以及颜色public void display(String color) {System.out.println("方块形状: " + getShape() + ": " + color);}
}

3.2.具体享元

IBox.java

public class IBox extends AbstractBox{@Overridepublic String getShape() {return "I";}
}

LBox.java

public class LBox extends AbstractBox{@Overridepublic String getShape() {return "L";}
}

OBox.java

public class OBox extends AbstractBox{@Overridepublic String getShape() {return "O";}
}

3.3.享元工厂

BoxFactory.java

//工厂类,将该类设计为单例
public class BoxFactory {private HashMap<String,AbstractBox> map;//在构造方法中进行初始化操作private BoxFactory(){map = new HashMap<String,AbstractBox>();map.put("I", new IBox());map.put("L", new LBox());map.put("O", new OBox());}private static BoxFactory factory = new BoxFactory();//提供一个方法获取该工厂类对象public static BoxFactory getInstance(){return factory;}//根据名称获取图形对象public AbstractBox getShape(String name){return map.get(name);}
}

3.4.测试

Client.java

public class Client {public static void main(String[] args) {//获取 I 图形对象AbstractBox box1 = BoxFactory.getInstance().getShape("I");box1.display("灰色");//获取 L 图形对象AbstractBox box2 = BoxFactory.getInstance().getShape("L");box2.display("红色");//获取 O 图形对象AbstractBox box3 = BoxFactory.getInstance().getShape("O");box3.display("黑色");//获取 O 图形对象AbstractBox box4 = BoxFactory.getInstance().getShape("O");box4.display("蓝色");System.out.println("两次获取到的 O 图形对象是否为同一个对象?" + (box3 == box4));}
}

输出结果如下:

方块形状: I: 灰色
方块形状: L: 红色
方块形状: O: 黑色
方块形状: O: 蓝色
两次获取到的 O 图形对象是否为同一个对象?true

4.优缺点

(1)享元模式的优点:

  • 减少内存使用:通过共享对象,减少了系统中的对象数量,从而节省了内存空间。
  • 提升性能:由于减少了对象的数量,可以提高系统的性能。共享的对象可以在多个上下文中共享使用,避免了重复创建对象的开销。
  • 简化操作:对于具有相同或相似内部状态的对象,可以共享一个享元对象,简化了对对象的操作。

(2)享元模式的缺点:

  • 共享对象状态的限制:由于享元模式需要将对象的内部状态和外部状态分离,因此可能会引入一定的限制。对于外部状态的改变,需要在外部进行设置,可能会带来一些额外的复杂性。
  • 对象共享的线程安全问题:如果多个线程同时使用享元对象,需要保证线程安全,避免对共享对象进行修改导致其他线程的错误结果。
  • 可能引入过多的复杂性:在使用享元模式时需要权衡对象的共享和不共享状态,可能会增加代码的复杂性和维护成本。

5.使用场景

(1)享元模式通常适用于以下场景:

  • 需要大量创建相似的对象:例如,用于图形编辑器中的大量相似图形元素(如矩形、圆形等),可以创建一个共享的图形元素对象池,避免每次创建图形元素时都进行对象的初始化,从而提高性能和减少内存占用。
  • 对象可以被共享复用:当一个对象需要在多个地方进行共享使用时,使用享元模式可以避免重复创建对象的开销,并且提高系统性能。
  • 对象的大部分状态可以外部化:如果一个对象的大部分状态可以被外部计算或者设置,可以使用享元模式来将内部状态与外部状态分离,避免重复创建对象的开销。
  • 系统需要缓存对象的场景:当系统需要缓存一些对象,并根据需要重复使用这些对象时,使用享元模式可以减少内存的使用,提高系统的性能。

(2)总之,享元模式适用于需要大量创建相似对象的场景,并且这些对象可以被共享复用,可以提高系统的性能和减少内存占用。

6.JDK 源码解析——Integer 类

相关面试题可以参考下面的文章:
Java 基础面试题——基本数据类型与包装类

Integer 类使用了享元模式。先看下面的例子:

class IntegerDemo {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));}
}

上述程序的运行结果如下:

i1 和 i2 是否为同一个对象?true
i3 和 i4 是否为同一个对象?false

为什么第一个输出语句输出的是 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_VALUEh = 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 对象。

相关文章:

Java 设计模式——享元模式

目录 1.概述2.结构3.实现3.1.抽象享元3.2.具体享元3.3.享元工厂3.4.测试 4.优缺点5.使用场景6.JDK 源码解析——Integer 类 1.概述 &#xff08;1&#xff09;享元模式 (Flyweight Pattern) 是一种结构型设计模式&#xff0c;主要通过共享对象来减少系统中的对象数量&#xff…...

再扩国产化适配版图,长安链新增数据库兼容性认证

基础软件的自主可控影响着国家信息安全与产业技术发展是否受制于人。实现真正长期可持续的技术自主可控&#xff0c;需要再进一步的是让国产化信息系统生态更有”活性“&#xff0c;基础软件之间、基础软件与其他国产上层应用的互相兼容、互相促进&#xff0c;共同繁荣发展&…...

MES系统数据集成系统源码

MES系统数据集成系统源码 1 MES的生产计划管理综述 生产计划的管理在MES系统中是非常重要的。我们知道&#xff0c;一般的情况下&#xff0c;企业的生产计划是由上层管理系统ERP来完成&#xff0c;那么MES中的生产计划和ERP的生产计划的不同点在于&#xff1a; ERP的生产计划…...

关于道一云-七巧使用感悟

低代码&#xff0c;开发快速&#xff0c;缺点组件比较少但能满足大多数场景&#xff0c;如果需要的话得自己造&#xff0c;官方的技术群里面普遍问题响应就很快&#xff0c;技术文档这个会抽风&#xff0c;有些代码就改个筛选条件本地死活运行不了&#xff0c;去官网那里问他们…...

去中心化数据云项目Oort主网即将上线

备受期待的去中心化数据云项目Oort&#xff0c;今日已通过官方X账号官宣&#xff0c;将于纽约时间11月27日正式上线主网Olympus Protocol。届时&#xff0c;用户也能够通过Oort的Ale Wallet钱包和开发者API使用主网&#xff0c;并可通过OORT浏览器查询交易和合约&#xff0c;开…...

CSS知识点梳理(一)

CSS 是层叠样式表&#xff08;Cascading Style Sheets&#xff09;的缩写&#xff0c;它是一种用于描述 HTML 或 XML&#xff08;包括如 SVG、MathML 等派生语言&#xff09;文档呈现的语言。CSS 可以用来控制文档的布局、颜色、字体等外观属性。 CSS 的主要特点包括&#xff…...

网络安全深入学习第八课——反向代理(工具:frp)

文章目录 一、实验环境二、实验要求三、开始模拟1、攻击机配置frp文件2、攻击拿下跳板机&#xff0c;并且上传frpc.ini、frpc.exe、frpc_full.ini文件3、把frps.ini、、frps.exe、frps_full.ini文件放到VPS主机上4、VPS机开启frp5、跳板机开启frp6、验证 一、实验环境 攻击机&…...

浅谈前端自定义VectorGrid矢量瓦片样式

目录 前言 一、VectorGrid相关API介绍 1、VectorGrid 2、 LayerStyles样式详解 二、样式自动配置 1、页面定义 2、地图及PBF瓦片引入 3、矢量瓦片样式定义 4、鼠标事件交互 三、最终效果 1、自定义样式展示 2、鼠标交互 总结 前言 在上一篇博客中&#xff0c;详细讲…...

Qt5多线程<12>

1.多线程的简单实现 <1> 在头文件threaddlg.h声明用于界面显示所需的控件,其代码如下: #ifndef THREADDLG_H #define THREADDLG_H#include <QDialog> #include <QPushButton> #include "workthread.h" #define MAXSIZE 5class ThreadDlg : publ…...

Linux学习笔记之五(父子进程、孤儿进程、僵尸进程、守护进程)

Linux 1、进程1.1、进程的六种状态1.2、创建子进程1.3、添加子进程任务1.4、孤儿进程、僵尸进程、守护进程1.4.1、避免僵尸进程1.4.2、创建守护进程1.4.3、杀死守护进程 1.5、综合练习 1、进程 进程可以简单的理解为一个正在执行的程序&#xff0c;它是计算机系统中拥有资源和…...

[题] 不容易系列之(3)―― LELE的RPG难题 #DP

题目 不容易系列之(3)―― LELE的RPG难题 思路 简单的DP题。 代码 #include<bits/stdc.h> using namespace std; //默认以0开头&#xff0c;以1和2结尾。f[i][1]表示长度为i的以1结尾的涂抹方案 //状态转移方程&#xff1a;若以1结尾&#xff0c;则前面一个格子只能是…...

pip 安装任意软件包报错

现象 使用 pip 命令时提示 查看源码 可以看到是从 pip 包中导入 main失败&#xff0c;点击查看目录 main 文件不见了&#xff0c;判断是文件缺失&#xff0c;重装 pip 即可 # python3 下载 pip curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py # python2 下载…...

NLP之Bert实现文本多分类

文章目录 代码代码整体流程解读debug上面的代码 代码 from pypro.chapters03.demo03_数据获取与处理 import train_list, label_list, val_train_list, val_label_list import tensorflow as tf from transformers import TFBertForSequenceClassificationbert_model "b…...

对话大众软件子公司:中国的智舱、智驾比欧洲早一代

作者 | 德新 编辑 | 王博 尤其在上海车展之后&#xff0c;大部分的外资车企都在转型调整。 2023年的上海车展是一个重要节点。在这之前&#xff0c;疫情阻断了国内和海外频繁的线下交流&#xff0c;而国内汽车的新能源化和智能化在这期间完成了一次飞跃式的发展。所以车展开…...

基于FPGA的图像RGB转HSV实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1. RGB与HSV色彩空间 4.2. RGB到HSV转换原理 5.算法完整程序工程 1.算法运行效果图预览 将FPGA的仿真结果导入到matlab中&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a …...

小型企业如何数字化转型?ZohoCRM助力小企业转型

小型企业数字化之路倍加艰难&#xff0c;其组织规模有限、资源有限&#xff0c;数字化布局或转型&#xff0c;也存在与数字平台匹配度的问题。其实小型企业可以通过CRM客户管理系统实现高效的客户关系管理&#xff0c;进一步提高市场竞争力。 建立高效易用的客户关系管理系统 …...

聊聊模板引擎<Template engine>

模板引擎是什么 模板引擎是一种用于生成动态内容的工具&#xff0c;通常用于Web开发中。它能够将静态的模板文件和动态数据结合起来&#xff0c;生成最终的HTML、XML或其他文档类型。模板引擎通过向模板文件中插入变量、条件语句、循环结构等控制语句&#xff0c;从而实现根据…...

多平台商品采集——API接口:支持淘宝、天猫、1688、拼多多等多个电商平台的爆款、销量、整店商品采集和淘客功能

item_get-获得淘宝商品详情 item_get_app-获得淘宝app商品详情原数据 item_get_pro-获得淘宝商品详情高级版 item_search-按关键字搜索淘宝商品 item_search_img-按图搜索淘宝商品&#xff08;拍立淘&#xff09; item_search_shop-获得店铺的所有商品 API请求地址 公共…...

UI自动化测试框架设计(Selenium)

...

towr code阅读

1. Introduction towr是非常优美的足式机器人规划代码&#xff0c;通过阅读towr重要的几个迭代版本的代码深入了解。 2 v0.1 第一代的版本&#xff0c;foot的位置是提前给定的&#xff0c;只对COG的trajectory进行优化。 2.1 cost 公式 仅仅只考虑加速度&#xff0c; ∫ …...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...