设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

55 | 享元模式(下):剖析享元模式在Java Integer、String中的应用

自定义缓存最大值
缓存范围
实现
重新实现IntegerCache类
垃圾回收问题
Java String的实现
Java Integer的实现
设计思路
字符串常量池
IntegerCache
Java对象在内存中的存储
自动装箱和自动拆箱
应用场景
实现
原理
课堂讨论
重点回顾
享元模式在Java String中的应用
享元模式在Java Integer中的应用
享元模式
剖析享元模式在Java Integer、String中的应用

该思维导图由 AI 生成,仅供参考

上一节课,我们通过棋牌游戏和文本编辑器这样两个实际的例子,学习了享元模式的原理、实现以及应用场景。用一句话总结一下,享元模式中的“享元”指被共享的单元。享元模式通过复用对象,以达到节省内存的目的。
今天,我再用一节课的时间带你剖析一下,享元模式在 Java Integer、String 中的应用。如果你不熟悉 Java 编程语言,那也不用担心看不懂,因为今天的内容主要还是介绍设计思路,跟语言本身关系不大。
话不多说,让我们正式开始今天的学习吧!

享元模式在 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 为基本数据类型提供了对应的包装器类型。具体如下所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java中的享元模式在Integer和String类中的应用是非常重要的。通过对Java中的自动装箱和自动拆箱的理解,我们可以看到Integer类利用享元模式来缓存-128到127之间的整型值,以节省内存空间。这种机制使得在创建整型对象时,可以复用已存在的对象,而不是每次都创建新的对象,从而提高了内存利用率。String类也利用享元模式来复用相同的字符串常量,通过JVM开辟的字符串常量池来存储字符串常量,从而节省内存空间。然而,享元模式对JVM的垃圾回收并不友好,因为享元工厂类一直保存了对享元对象的引用,导致对象即使在没有任何代码使用的情况下也不会被JVM垃圾回收机制自动回收掉。因此,在某些情况下,利用享元模式可能会浪费更多的内存。除非经过线上验证,利用享元模式真的可以大大节省内存,否则,就不要过度使用这个模式。文章还提出了一个思路,即在程序的运行过程中,当用到某个整型对象的时候,创建好放置到IntegerCache,下次再被用到的时候,直接从IntegerCache中返回,并且能够做到在某个对象没有任何代码使用的时候,能被JVM垃圾回收机制回收掉。这篇文章通过具体的代码示例和内存存储结构图的解释,帮助读者理解了Java中享元模式的应用,适合Java开发者快速了解享元模式在Integer和String中的应用,以及如何在实际开发中充分利用这一特性来提升性能。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(48)

  • 最新
  • 精选
  • 一丨丿丶乙
    享元--->复用,线程池等。通过复用对象,以达到节省内存的目的 1.懒加载,dubble check 2.weak reference持有享元对象

    作者回复: 嗯嗯 ������

    2020-11-18
    1
  • 张三丰
    为什么说垃圾回收的时候如果保存了对象的"引用"就不友好,垃圾回收的依据不是只看这个对象还有没有被"使用"吗?

    作者回复: 有引用,就是在被使用啊

    2020-07-31
  • Liam
    享元池用weak reference持有享元对象
    2020-03-09
    7
    75
  • 小晏子
    如果IntegerCache不事先指定缓存哪些整形对象,那么每次用到的时候去new一个,这样会稍微影响一些效率,尤其在某些情况下如果常用到-128~127之间的数,可能会不停的new/delete, 不过这个性能问题在大部分时候影响不是很大,所以按照string的设计思路也是可行的, 按照这个思路设计IntegerCache类的话,如下 private static class IntegerCache { public static final WeakHashMap<Integer, WeakReference<Integer>> cache = new WeakHashMap<Integer, WeakReference<Integer>>(); //也可以提前分配容量 private IntegerCache(){} } public static Integer valueOf(int i) { final WeakReference<Integer> cached = IntegerCache.cache.get(i); if (cached != null) { final Integer value = cached.get(i); if (value != null) { return value; } } WeakReference<Integer> val = new WeakReference<Integer>(i); IntegerCache.cache.put(i, val); return val.get(); }
    2020-03-09
    7
    51
  • 辣么大
    谢谢各位的讨论,今天学到了软引用,弱引用,和WeakHashMap。内存吃紧的时候可以考虑使用WeakHashMap。 https://www.baeldung.com/java-weakhashmap https://www.baeldung.com/java-soft-references https://www.baeldung.com/java-weak-reference
    2020-03-11
    7
    47
  • 李小四
    设计模式_55: # 作业 原来还有个WeakHashMap,学习了。 # 感想 自己尝试了写了一个,然后分别测试了10,000次、100,000次,1,000,000次创建,value从1-100,100-200,10000-10100,发现不管哪个场景,总是JVM的Integer时间更短,我写的要3倍左右的时间,不禁感叹,Java二十几年了,大部分的优化应该都做了,不要期望自己花20分钟能改出超过JVM的性能。
    2020-03-17
    30
  • 3Spiders
    课后题。因为整型对象长度固定,且内容固定,可以直接申请一块连续的内存地址,可以加快访问,节省内存?而String类不行。
    2020-03-09
    1
    25
  • Geek_41d472
    我勒个擦 ,这好像是我碰到的两道面试题,包装和拆箱这道题简直就是个坑,有踩坑的举个手
    2020-03-10
    13
  • webmin
    抛砖引玉实现了一个有限范围的缓存(-128~2048383(127 * 127 * 127)) public class IntegerCache { private static final int bucketSize = 127; private static final int level1Max = bucketSize * bucketSize; private static final int max = bucketSize * bucketSize * bucketSize; private static final WeakHashMap<Integer, WeakHashMap<Integer, WeakHashMap<Integer,WeakReference<Integer>>>> CACHE = new WeakHashMap<>(); public static Integer intern(int integer) { if (integer <= 127) { return integer; } if (integer > max) { return integer; } synchronized (CACHE) { Integer l1 = 0; int tmp = integer; if(integer >= level1Max){ l1 = integer / level1Max; integer -= level1Max; } Integer l2 = integer / bucketSize; Integer mod = integer % bucketSize; WeakHashMap<Integer, WeakHashMap<Integer,WeakReference<Integer>>> level1 = CACHE.computeIfAbsent(l1, val -> new WeakHashMap<>()); WeakHashMap<Integer,WeakReference<Integer>> level2 = level1.computeIfAbsent(l2, val -> new WeakHashMap<>()); WeakReference<Integer> cache = level2.computeIfAbsent(mod, val -> new WeakReference<>(tmp)); Integer val = cache.get(); if (val == null) { val = integer; level2.put(mod, new WeakReference<>(val)); } return val; } } public static int integersInCache() { synchronized (CACHE) { int sum = CACHE.size(); for (Integer key : CACHE.keySet()) { WeakHashMap<Integer, WeakHashMap<Integer,WeakReference<Integer>>> tmp = CACHE.get(key); sum += tmp.size(); for(Integer l2Key : tmp.keySet()) { sum += tmp.get(l2Key).size(); } } return sum; } } }
    2020-03-09
    1
    10
  • Eden Ma
    突然理解OC中NSString等也用到了享元设计模式.
    2020-03-09
    2
    9
收起评论
显示
设置
留言
48
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部