设计模式学习笔记 - 设计模式与范式 -结构型:8.享元模式(下):享元模式在Java Integer、String中的应用

概述

上篇文章《结构型:7.享元模式(上):享元模式原理和应用》,通过棋牌游戏和文本编辑器的例子,学习了享元模式的原理、实现以及应用场景。用一句话总结下,享元模式中的 “享元” 指被共享的单元。享元模式通过复用对象,已达到节省内存的目的。

本章,带你剖析一下享元模式在 Java Integer、String中的应用。


享元模式在 Java Integer 中的应用

先看下面这段代码。先思考下,这段代码会输出什么样的结果。

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

如果你不熟悉 Java,你可能会觉得 i1 和 i2 值都是 56,i3 和 i4 值都是 129,所以 i1 跟 i2 值相等, i3 跟 i4 值相等,所以输出的结果应该是两个 true,这个图的分析是不对的,主要还是因为你对 Java 语法不熟悉。要正确分析上面的代码,需要弄清楚下面两个问题。

  • 如何按断两个 Java 对象是否相等(也就是代码中 “==” 操作符的含义)?
  • 什么事自动装箱(Autoboxing)和自动拆箱(Unboxing)?

Java 为基本数据类型提供了对应地包装器类型,具体如下表所示。

基本数据类型对应地包装器类型
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
shortShort
byteByte
charCharacter

所谓自动装箱,就是自动将基本数据类型转换为包装器类型。所谓的自动拆箱,也就是自动将包装器类型转化为基本数据类型。具体代码如下所示:

Integer i = 56; // 自动装箱
int j = i; // 自动拆箱

数值 56 是基本数据类型,当赋值给包装类型(Integer) 变量的时候,触发自动装箱操作,创建一个 Integer 类型的对象,并赋值给变量 i。其底层相当于执行了下面这条语句:

Integer i = 56; 底层执行了 Integer i = Integer.valueOf(56);

反过来,当把包装器类型的变量 i,赋值给基本数据类型的变量 j 时,触发自动拆箱操作,将 i 中的数据取出,赋值给 j。其底层相当于执行了下面这条语句:

int j = i; 底层执行了 int j = i.intValue();

弄清楚了自动装箱和拆箱,再看下,如何判定两个类型是否相等? 在此之前,我们要先搞清楚,Java 对象在内存中是如何存储的。通过下面的例子来说明下。

User a = new User(123,123);

针对这条语句,我画了一张内存存储结构图,如下所示。 a 存储的值是 User 对象的内存地址,在图中表现为 a 指向 User 对象。

在这里插入图片描述
通过 “==” 来判定两个对象是否相等时,实际上是在判断两个局部变量存储的地址是否相同,换句话说,是在判断两个局部变量是否指向相同的对象。

我们在重新看下开头的那段代码。

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

前 4 行触发自动装箱操作,也就是会创建 Integer 对象,并赋值给 i1、i2、i3、i4 这四个变量。根据刚刚的讲解,i1、i2 尽管存储的值都是 56,但是指向不同的 Integer 对象,所以通过 “==” 来判定是否相同时,会返回 false。同理,i3 == i4 判定语句也会返回 fasel。

但是,上面的分析还是不对,答案并非是两个 false,而是一个 true,一个 fasle。这正是因为 Integer 用到了享元模式来复用对象,才会导致这样的运行结果。当我们自动装箱,也就是调用 valueOf() 来创建 Integer 对象的时候,如果要创建的对象的值在 -128~127 之间,会从 IntegerCache 类中直接返回,否则才会调用 new 方法创建。

    public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}

实际上,这里的 IntegerCache 相当于上篇文章的生成享元对象的工厂类,只不过名字不叫 xxxFactory 而已。我们来看下 IntegerCache 的具体代码实现,它是 Integer 的内部类,你可以自行查看 JDK 源码。

    /*** 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 只缓存 -128~127 之间的整型数呢?

IntegerCache 的代码实现中,当这个类被加载的时候,缓存的享元对象会被集中一次性的创建好。我们不可能在 IntegerCache 类中预先创建好所有的整型值,这样既占用太多内存,也使得 IntegerCache 类的加载时间过长。所以,只选择缓存对于大部分应用来说最常用的整型值,也就是一个字节的大小(-128~127 之间的数据)。

实际上,JDK 也提供了方法让我们可以自定义缓存的最大值,有下面两种方式。

// 方法一
-Djava.lang.Integer.IntegerCache.high=255
// 方法二
-XX:AutoBoxCacheMax=255

如果你通过分析应用的 JVM 内存占用情况,发现 -128~255 之间的数据占用的内存比较多,你就可以采用上面的方式,将缓存的最大值从 127 调整至 255。不过, JDK 并没有提供最小值的设置方法。

现在,再回到最开始的问题,因为 56 处于 -128~127 之间,i1、i2 会指向相同的享元对象,所以 i1 == i2 会返回 true。而 128 大于 127,并不会被缓存,每次都会创建一个全新的对象,也就是说 i3、i4 会指向不同的 Integer 对象,所以 i3 == i4 会返回 false。

除了 Integer 之外,其他包装器类型,比如 LongShortByte 等,也都利用了享元模式来缓存 -128~127 之间的数据。比如,Long 类型对应的 LongCache 享元工厂类及 valueOf() 函数代码如下所示:

	private static class LongCache {private LongCache(){}static final Long cache[] = new Long[-(-128) + 127 + 1];static {for(int i = 0; i < cache.length; i++)cache[i] = new Long(i - 128);}}/*** Returns a {@code Long} instance representing the specified* {@code long} value.* If a new {@code Long} instance is not required, this method* should generally be used in preference to the constructor* {@link #Long(long)}, as this method is likely to yield* significantly better space and time performance by caching* frequently requested values.** Note that unlike the {@linkplain Integer#valueOf(int)* corresponding method} in the {@code Integer} class, this method* is <em>not</em> required to cache values within a particular* range.** @param  l a long value.* @return a {@code Long} instance representing {@code l}.* @since  1.5*/public static Long valueOf(long l) {final int offset = 128;if (l >= -128 && l <= 127) { // will cachereturn LongCache.cache[(int)l + offset];}return new Long(l);}

我们平时的开发中,对于下面这样三种创建整型对象的方式,优先使用后两种。

Integer a = new Integer(123);
Integer a = 123;
Integer a = Integer.valueOf(123);

第一种创建方式不会用到 IntegerCache,而后两种创建方法可以利用 IntegerCache 缓存返回共享的对象,已达到节省内存的目的。

享元模式在 Java String 中的应用

刚刚我们讲了享元模式在 Java Integer 类中的应用,现在在看下享元模式在 Java String 类中的应用。同样,还是先来看一段代码,你觉得这段代码输出的结果是什么呢?

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

上面代码运行的结果一个是 true,一个是false。跟 Integer 的设计思路类似,String 类利用享元模式来复用相同的字符串常量(也就是代码中的 “Hello”)。JVM 会专门开辟一块存储区来存储字符串常量,这块存储区叫做 “字符串常量池”。上面代码对应的内存存储结构如下所示:

在这里插入图片描述
不过 String 类的享元模式的设计,跟 Integer 类稍微不同。Integer 类中要共享的对象,是在类加载的时候,就集中一次性地创建好。但是,对于字符串来说,没法事先知道要共享哪些字符串常量,只能在某个字符串常量第一次被用到的时候,存储到常量池中,当之后再用到的时候,直接引用常量池中已存在的即可,就不需要再重新创建了。

总结

在 Java Integer 的实现中,-128 到 127 之间的整型数会被实现创建好,缓存在 IntegerCache 类中。当我们使用自动装箱或者 valueOf() 来创建这个数值区间的整型对象时,会复用 IntegerCache 类事先创建好的对象。这里的 IntegerCache 类就是 享元工厂类,事先创建好的整型对象就是享元对象。

在 Java String 类的实现中,JVM 开辟一块存储区专门存储字符串常量,这块存储区叫做字符串常量池,类似于 Integer 中的 IntegerCache。不过,跟 IntegerCache 不同的是,它并非事先创建好需要共享的对象,而是在程序运行期间,根据需要来创建和缓存字符串常量。

此外,还要补偿强调一下。

享元模式对 JVM 的垃圾回收并不友好。因为享元工厂类一直保持了对享元对象的引用,这就导致享元对象在没有任何代码使用的情况下,也并不会被 JVM 垃圾回收机制自动回收掉。因此,在某些情况下,如果对象生命周期很短,也不会被密集使用,利用享元模式反倒可能会浪费更多的内存。所以,除非经过线上验证,利用享元模式真的可以大大节省内存,否则,就不要过度使用这个模式,为了一点点内存的节省而引入一个复杂的设计,得不偿失。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

这次轮到小米,遥遥领先!

年轻人的第一辆保时米 3 月28日晚小米首款汽车小米汽车 SU7 正式发布并上市&#xff0c;新车定位于“C 级高性能生态科技轿车”&#xff0c;提供双电机版本和单电机版本车型选择&#xff0c;并提供容量为 73.6 千瓦时以及 101 千瓦时电池可选&#xff0c;售价 21.59 万元-29.99…

ArcObject for Java 开发环境搭建(IDEA2021)

一、系统环境 1、JDK 需要32位JDK1.8&#xff0c;安装版或离线免安装版均可。 arcgis10.2.2使用1.8.0_151版本&#xff0c;arcgis10.7使用1.8.0_181版本&#xff0c;不过应该都可以&#xff0c;小版本没有严格区分。 2、配置环境变量 配置Arcgis环境变量&#xff08;必须&…

CXL系统架构

CXL系统架构 CXL支持三种设备类型&#xff0c;如下图。Type 1支持CXL.cache和CXL.io&#xff1b;Type 2支持CXL.cache&#xff0c;CXL.mem和CXL.io&#xff1b;Type 3支持CXL.mem和CXL.io。无论哪种类型&#xff0c;CXL.io都是不可缺少的&#xff0c;因为设备的发现&#xff0…

springcloud微服务项目,通过gateway+nacos实现灰度发布(系统不停机升级)

一、背景 灰度发布的目的是保证系统的高可用&#xff0c;不停机&#xff0c;提升用户体验。在微服务系统中&#xff0c;原有系统不下线&#xff0c;新版系统与原有系统同时在线&#xff0c;通过访问权重在线实时配置&#xff0c;可以让少量用户先应用新版本功能&#xff0c;如…

JavaSE day14笔记

第十四天课堂笔记 课上: 适当做笔记课下 : 总结 , 读代码 , 反复敲代码 , 做练习 数组★★★ 数组 : 存储多个 同一类型 的容器格式 :数组类型 : 引用数据类型, new运算符在堆中 分配一块连续的存储空间 , 系统会给数组元素默认初始化 , 将该数组的引用赋值给数组名 引用数据…

Java中读取html文件转成String,展示在浏览器

这里写目录标题 第一章1.1&#xff09;pom中引入依赖和html文件示例1.2&#xff09;使用hutool工具包读取html文件转为string1.3&#xff09;页面显示 第一章 1.1&#xff09;pom中引入依赖和html文件示例 引入hutool工具包依赖 <dependency><groupId>cn.hutool&…

mongodb sharding分片模式的集群数据库,日志治理缺失导致写入数据库报错MongoWriteConcernException的问题总结(上)

一、背景 常见的mongodb集群模式有以下三种&#xff1a; 主从复制&#xff08;Master-Slave&#xff09;模式副本集&#xff08;Replica Set&#xff09;模式分片&#xff08;Sharding&#xff09;模式 公司测试环境搭建的集群采用分片模式&#xff0c;有同事反馈说&#xf…

Qt打印系统库的日志 - QLoggingCategory

Qt的动态库通过源码可以可以看到含有大量的qCInfo 和 qCDebug 等大量的日志&#xff0c; 但是我们正常运行Qt程序&#xff0c;这些动态库或插件里面的日志是不会输出到我们的控制台里面的。 所以本章主要记录怎么输出这些日志出来。 一&#xff1a; 步骤 主要使用的是Qt的 函…

ABA分析方法 — 软硬件开发必备的一种分析问题绝佳套路

ABA分析方法 “决定我们看到什么&#xff0c;并且看到什么方式的是我们的思维方式。” — 乔治奥威尔 更多精彩内容&#xff0c;请关注公众号《机器灵魂注入师》。 什么是ABA分析方法&#xff1f; 可能你从很多地方听过这个名词&#xff0c;无论是在软件开发还是硬件领域(比较…

单片机之串口通信

目录 串口介绍 通信的基本概念 并行通信和串行通信 同步通信和异步通信 串行异步通信方式 串行同步通信方式 通信协议 单片机常见通信接口 串行通信三种模式 串口参数 传输速度 ​串口的连接 电平标准 串行口的组成 串口数据缓冲寄存器 串行口控制寄存器 串口…

HarmonyOS入门--配置环境 + IDE汉化

文章目录 下载安装DevEco Studio配置环境先认识DevEco Studio界面工程目录工程级目录模块级目录 app.json5module.json5main_pages.json通知栏预览区 运行模拟器IED汉化 下载安装DevEco Studio 去官网下载DevEco Studio完了安装 配置环境 打开已安装的DevEco Studio快捷方式…

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查 关系数据库 关系对象数据库&#xff08;ORM&#xff09; 应用偏好数据库 分布式数据库 关系型数据库&#xff08;Relational Database&#xff0c;RDB&#xff09;是一种基于关系模型来管理数据的数据库。HarmonyOS关系型…

阿里云Salesforce CRM功能差异列表 - Winter‘ 24

阉割版的阿里云Salesforce由于技术和监管等因素与国际版的Salesforce差距很大&#xff01; 一、Winter‘ 24版差异概况&#xff1a; 1.1. 主要版本&#xff1a; 阿里云上的 Salesforce 提供两个版本&#xff0c;用于生产用途的 CN 版本&#xff08;CN Edition&#xff09;和用…

NineData与StarRocks商业化运营公司镜舟科技完成产品兼容认证

近日&#xff0c;镜舟科技与NineData完成产品兼容测试。在经过联合测试后&#xff0c;镜舟科技旗下产品与NineData云原生智能数据管理平台完全兼容&#xff0c;整体运行高效稳定。 镜舟科技致力于帮助中国企业构建卓越的数据分析系统&#xff0c;打造独具竞争力的“数据护城河”…

乐维更改IP地址

1.1 系统IP调整 vim /etc/sysconfig/network-scripts/ifcfg-ens1921.2 Web相关服务IP变更 1.2.1 编辑/itops/nginx/html/lwjkapp/.env文件,更改ZABBIXSERVER、ZABBIXRPCURL、DB_HOST中的IP 1.2.2 进入/itops/nginx/html/lwjk_app/目录下,执行php bin/manager process-conso…

VMware虚拟机更换引导顺序

前言 我用wmware装了黑群晖测试&#xff0c;将img转成vmdisk的格式之后发现系统引导盘之后1G&#xff0c;有点太小了 我准备把wmware的黑群晖系统迁移到新添加的虚拟磁盘里 1.登录黑群晖的SSH 请先在黑群晖的控制面板中的终端机和SNMP里面启用SSH功能&#xff0c;才能使用ss…

原创度检测工具分享,文章质量检测方便又简单

文章检测有利于我们了解文章内容的质量高低&#xff0c;而在以往我们检测文章只能依靠手动去检测&#xff0c;这是相当消耗工作时间的&#xff0c;但是在原创度检测工具出来之后&#xff0c;很多人开始检测文章质量就改用原创度检测工具了&#xff0c;因为使用原创度检测工具是…

睿尔曼超轻量仿人机械臂之复合机器人底盘介绍及接口调用

机器人移动平台是一个包含完整成熟的感知、认知和定位导航能力的轮式机器人底盘产品级平台&#xff0c;产品致力于为各行业细分市场的商用轮式服务机器人提供一站式移动机器人解决方案&#xff0c;让合作伙伴专注在核心业务/人机交互的实现。以下是我司产品双臂机器人以及复合升…

亚马逊云科技如何看待云计算产业在硬件方面的创新趋势

“真正认真对待软件的人应该制造自己的硬件。” 这是被称为个人电脑之父的艾伦凯博士的一句广为流传的言论。 这种趋势正在云计算行业发生。 目前&#xff0c;全球主流云计算厂商均已加入自研芯片的行列。 如果追溯这一趋势的发展&#xff0c;亚马逊云技术无疑是领先者。 在r…

酒店能源监测管理系统:实现节能减排与提升管理效率的利器

随着全球能源问题的日益突出和可持续发展理念的深入人心&#xff0c;酒店业也在积极探索节能减排的途径。在这一背景下&#xff0c;酒店能源监测管理系统应运而生&#xff0c;成为了酒店行业提升管理效率、降低能源消耗的重要工具。本文将从多个角度介绍酒店能源监测管理系统的…