结构型(三) - 享元模式

一、概念

享元模式(Flyweight Pattern):所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。

优点:可以极大地减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。

缺点:享元对象会一直被享元工厂类引用,不利于JVM互收,除非经过线上验证,利用享元模式真的可以大大节省内存,否则,就不要过度使用这个模式。

使用场景:我们在需要创建大量(例如10^5)的相似的对象时,使用享元模式,把不可变的对象单独抽出来,进行共享可以减少内存消耗。不仅仅相同对象可以设计成享元,对于相似对象,我们也可以将这些对象中相同的部分(字段),提取出来设计成享元,让这些大量相似对象引用这些享元。

二、实现

虽然我不玩游戏,但是这里举一个游戏的例子,一般比较火的游戏会有比较大的访问量。比如某一个游戏,游戏人物都可以选武器,游戏中的武器就那么几种,比如无影剑。所有玩家都可以选择这个武器,由于这个武器一旦上线一般不会改动,它内部的杀伤力等配置都是固定的,所以没必要为每一个玩家新建一个武器对象,这个武器就可以设置为全局唯一享元对象。

1、武器装备抽象类

public interface Weaponry {void releaseLethality();
}

2、武器装备无影剑类(这名起的真俗,凑合看吧)

public class ShadowlessSword implements Weaponry {private String name;private int lethality;public ShadowlessSword(String name, int lethality) {this.name = name;this.lethality = lethality;}@Overridepublic void releaseLethality() {System.out.println("释放杀伤力: " + lethality);}
}

3、武器装备工厂,新建一个武器,共所有玩家共享。

public class WeaponryFactory {private static final Map<String, Weaponry> weaponries = new HashMap<>();static {weaponries.put("无影剑", new ShadowlessSword("无影剑", 100));}public static Weaponry getWeaponry(String name){return weaponries.get(name);}
}

4、玩家类

public class Gamers {private Weaponry weaponry;public Gamers(Weaponry weaponry) {this.weaponry = weaponry;}public void startUseWeaponry() {weaponry.releaseLethality();}public Weaponry getWeaponry(){return weaponry;}
}

5、测试类

public class Client {public static void main(String[] args) {Gamers gamers1 = new Gamers(WeaponryFactory.getWeaponry("无影剑"));gamers1.startUseWeaponry();Gamers gamers2 = new Gamers(WeaponryFactory.getWeaponry("无影剑"));gamers2.startUseWeaponry();System.out.println("武器是否一样:" + (gamers1.getWeaponry() == gamers2.getWeaponry()));}
}

6、运行结果
享元模式运行结果.png

三、享元模式在 Java 中的应用

1、在Integer中的使用,看下面代码,多么熟悉的面试题。答案输出是true和false。

Integer i1 = 56;
Integer i2 = 56;
Integer i3 = 129;
Integer i4 = 129;System.out.println(i1 == i2);
System.out.println(i3 == i4);

分析:Integer 是一个对象,赋值的时候会自动装箱,变成对象。按理说i1i2是不同对象,应该不等才对,之所以结果相等是因为:Integer 用到了享元模式来复用对象。

  • 执行代码Integer i1 = 56; 底层会调用valueOf方法。
Integer i = Integer.valueOf(59);
  • valueOf方法代码如下,当输入的值在一定范围的时候,会直接从IntegerCache中获取。
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);
}
  • 这里的 IntegerCache 相当于生成享元对象的工厂类,提前创建了-128 - 127之前的整数对象,所以上面的结果一个是true,一个是false。56的对象是直接从IntegerCache 中获取到的并非新建对象。除了 Integer 类型之外,其他包装器类型,比如 LongShortByte 等,也都利用了享元模式来缓存 -128 到 127 之间的数据。
/*** Cache to support the object identity semantics of autoboxing for values between* -128 and 127 (inclusive) as required by JLS.** The cache is initialized on first usage.  The size of the cache* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.* During VM initialization, java.lang.Integer.IntegerCache.high property* may be set and saved in the private system properties in the* sun.misc.VM class.*/
private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint 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) {// If the property cannot be parsed into an int, ignore it.}}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() {}
}
  • 这个最大阈值是可以改的,修改方法在IntegerCache代码上面的注释了有,也不用记。
 -XX:AutoBoxCacheMax=<size>
  • 如下三种声明方式,后两种都会从缓存种拿,第一种会新建一个对象,所以推荐使用后两种进行声明。
Integer a = new Integer(33);
Integer a = 33;
Integer a = Integer.valueOf(33);

2、在String中的使用。看下面代码,和上面一样,答案输出是truefalse

String s1 = "哈哈";
String s2 = "哈哈";
String s3 = new String("哈哈");System.out.println(s1 == s2);
System.out.println(s1 == s3);

Integer 类中要共享的对象,是在类加载的时候,就集中一次性创建好的。但是,对于字符串来说,我们没法事先知道要共享哪些字符串常量,所以没办法事先创建好,只能在某个字符串常量第一次被用到的时候,存储到常量池中,当之后再用到的时候,直接引用常量池中已经存在的即可,就不需要再重新创建了。

参考文章:
极客时间《设计模式》(王争)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/47273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql通过binlog日志恢复误删数据

1、先查看binlog功能是否开启 show variables like %log_bin%;log_bin为ON说明可以使用binlog恢复&#xff0c;如果为OFF说明没有开启binlog。 2、删除部分数据做测试 3、查找binlog文件位置 show variables like %datadir%;cd /var/lib/mysqlls -l删除数据时间是在文件154与…

redis数据类型详解+实例

redis中的数据类型&#xff1a; string&#xff0c;list, set, zset, hash,bitmaps, hyperloglog, gepspatial 目录 一、 String 二、List 三、Set 四、Zset 五、Hash 六、Bitmaps 七、Hyperloglog 八、Gepspatial 一、 String redis最基本的数据类型&#xff0c;一个…

微信小程序使用云存储和Markdown开发页面

最近想在一个小程序里加入一个使用指南的页面&#xff0c;考虑到数据存储和减少页面的开发工作量&#xff0c;决定尝试在云存储里上传Markdown文件&#xff0c;微信小程序端负责解析和渲染。小程序端使用到一个库Towxml。 Towxml Towxml是一个可将HTML、Markdown转为微信小程…

一体全栈、开箱即用!麒麟信安与灵雀云携手打造“操作系统+云平台”联合解决方案

近日麒麟信安与北京凌云雀科技有限公司&#xff08;以下简称“灵雀云”&#xff09;开展生态合作&#xff0c;共同完成了灵雀云企业级全栈云原生平台ACPV3与麒麟信安操作系统V3等系列产品的兼容性认证测试。基于双方产品兼容性良好、稳定运行、性能表现卓越&#xff0c;麒麟信安…

Java实现钉钉企业内部应用机器和自定义机器人发送消息

前言 公司让写一个服务监控的功能,当监测到服务停止时,向钉钉群里推送报警信息。之前大概看到钉钉的开放平台的API文档,好像能群发消息的只有机器人。 钉钉开放平台目前提供三种机器人: 企业内部应用机器人 群模板机器人 自定义机器人 本来向用自己比较熟悉的自定义机器人…

PHP自己的框架实现操作成功失败跳转(完善篇四)

1、实现效果&#xff0c;操作成功后失败成功自动跳转 2、创建操作成功失败跳转方法CrlBase.php /**成功后跳转*跳转地址$url* 跳转显示信息$msg* 等待时间$wait* 是否自动跳转$jump*/protected function ok($urlNULL,$msg操作成功,$wait3,$jump1){$code1;include KJ_CORE./tp…

【快速解决方案】浏览器的安全策略不允许通过 file:// 协议直接加载外部文件(最省事的方法)

目录 问题摘要 解决办法 检验结果 问题摘要 Failed to load resource: net::ERR_FILE_NOT_FOUND&#x1f308; Cute Code Editor &#x1f308;.html:162 Fetch API cannot load file:///D:/%E6%A1%8C%E9%9D%A2/%E4%B8%83%E5%A4%95%E5%BF%AB%E4%B9%90/index.txt. URL scheme …

Spring Boot

前言 什么是Spring Boot&#xff1f;为什么要学Spring Boot&#xff1f; Spring 的诞⽣是为了简化Java 程序的开发的&#xff0c;⽽Spring Boot 的诞⽣是为了简化Spring 程序开发 的。Spring就像汽车&#xff0c;相比以前人只能其自行车走路&#xff0c;汽车可以帮助人们更快…

C# WPF ListBox 动态显示图片

前言 最近在和其他软件联合做一个本地图片选择传输功能&#xff0c;为此希望图片能够有序的呈现在客户端&#xff0c;简单的实现了一下功能&#xff0c;通过Mvvm模式进行呈现&#xff0c;过程简单通俗&#xff0c;话不多说直接上图。 处理过程 前台代码 你只需要粘贴到你的前台…

生物识别技术与身份认证:探讨生物识别技术在强化身份认证和访问控制方面的应用

第一章&#xff1a;引言 在数字化时代&#xff0c;随着信息技术的飞速发展&#xff0c;身份认证和访问控制变得越来越重要。传统的用户名和密码方式逐渐暴露出安全性不足的问题&#xff0c;为此&#xff0c;生物识别技术应运而生。生物识别技术利用人体生物特征来识别个体身份…

第9步---MySQL的索引和存储引擎

第9步---MySQL的索引和存储引擎 1.索引 1.1分类 索引可以快速的找出具有特定值的行。不用从头开始进行寻找了。 类别 hash和btree hash 根据字段值生生成一个hash的值 快速的进行定位到对应的行的值 可能会出现相同的值&#xff0c;找到对应的空间会出现对应的值 btree树…

LTMC S/4HANA 2022 – 迁移您的数据

翻译一篇&#xff0c;估计很少人用过这个LTMC功能&#xff0c;更不用说&#xff0c;LTMOM了。一个还没开始用已经被弃用的事务代码&#xff1a; 在这篇博文中&#xff0c;我将解释如何在 S/4HANA 2022 版本中通过“迁移您的数据”应用程序逐步执行数据迁移。如您所知&#xff0…

5.7.webrtc线程的启动与运行

那在上一节课中呢&#xff1f;我向你介绍了web rtc的三大线程&#xff0c;包括了信令线程&#xff0c;工作线程以及网络线程。那同时呢&#xff0c;我们知道了web rtc 3大线程创建的位置以及运行的时机。 对吧&#xff0c;那么今天呢&#xff1f;我们再继续深入了解一下&#…

Redis分布式缓存

分布式缓存 -- 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题&#xff1a; 1.Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化 AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#x…

Certify The Web (IIS)

一、简介 Certify The Web 适用于 Windows的SSL 证书管理器用户界面&#xff0c;与所有 ACME v2 CA 兼容&#xff0c;为您的 IIS/Windows 服务器轻松地安装和自动更新来自 Letencrypt.org 和其他 ACME 证书授权机构的免费 SSL/TLS 证书&#xff0c;设置 https 从未如此简单。 …

JSON的处理

1、JSON JSON(JavaScript Object Notation)&#xff1a;是一种轻量级的数据交换格式。 它是基于 ECMAScript 规范的一个子集&#xff0c;采用完全独立于编程语言的文本格式来存储和表示数据。 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。易于人阅读和编写&#…

[PyTorch][chapter 49][创建自己的数据集 1]

前言&#xff1a; 后面几章主要利用DataSet 创建自己的数据集&#xff0c;实现建模&#xff0c; 训练&#xff0c;迁移等功能。 目录: pokemon 数据集深度学习工程步骤 一 pokemon 数据集介绍 1.1 pokemon: 数据集地址&#xff1a; 百度网盘路径: https://pan.baidu.com/s/1…

二、8.系统调用、可变参数和堆内存管理

系统调用&#xff1a;让用户进程申请操作系统的帮助 一个系统功能调用分为两部分&#xff0c; 一部分是暴露给用户进程的接口函数&#xff0c;它属于用户空间&#xff0c;此部分只是用户进程使用系统调用的途径&#xff0c;只负责发需求。另一部分是与之对应的内核具体实现&am…

C++day1(笔记整理)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.第一个c程序&#xff1a;hello world #include <iostream> //#:预处理标识符 //<iostream>:输入输出流类所在的头文件 //istream:输入流类 //ostream:输出流类using namespace std; //std&#x…

Goland 注释时自动在注释符号后添加空格

不得不说 JetBrains 旗下的 IDE 都好用&#xff0c;而且对于注释这块&#xff0c;使用 Ctrl / 进行注释的时候&#xff0c;大多会在每个注释符号后统一添加一个空格&#xff0c;比如 PyCharm 和 RubeMine 等。 # PyCharm # print("hello world") # RubyMine # req…