《设计模式之美》享元模式(下):剖析享元模式在Java Integer、String中的应用
发布日期:2021-07-21 00:20:47
浏览次数:9
分类:技术文章
本文共 2470 字,大约阅读时间需要 8 分钟。
学习笔记
享元模式在Java Integer中的应用
代码片段
Integer i1 = 56;Integer i2 = 56;Integer i3 = 129;Integer i4 = 129;System.out.println(i1 == i2);System.out.println(i3 == i4);
什么是自动装箱(Autoboxing)和自动拆箱(Unboxing)?
- 所谓的自动装箱,就是自动将基本数据类型转换为包装器类型。
- 所谓的自动拆箱,也就是自动将包装器类型转化为基本数据类型。
Integer i = 56; //自动装箱int j = i; //自动拆箱
- 数值56是基本数据类型 int,当赋值给包装器类型(Integer)变量的时候,触发自动装箱操作,创建一个 Integer 类型的对象,并且赋值给变量 i。其底层相当于执行了下面这条语句:Integer i = Integer.valueOf(56);
- 反过来,当把包装器类型的变量 i,赋值给基本数据类型变量 j 的时候,触发自动拆箱操作,将 i 中的数据取出,赋值给 j。其底层相当于执行了下面这条语句:int j = i.intValue();
Java对象在内存中是如何存储的
User a = new User(123, 23); // id=123, age=23
- a 存储的值是 User 对象的内存地址,在图中就表现为 a 指向 User 对象。
如何判定两个Java对象是否相等(也就代码中的“==”操作符的含义)?
- 当我们通过“==”来判定两个对象是否相等的时候,实际上是在判断两个局部变量存储的地址是否相同,换句话说,是在判断两个局部变量是否指向相同的对象。
代码片段初步分析
- 前4行赋值语句都会触发自动装箱操作,也就是会创建 Integer 对象并且赋值给i1、i2、i3、i4这四个变量。
- 根据刚刚的讲解,i1、i2尽管存储的数值相同,都是56,但是指向不同的 Integer 对象,所以通过“ == ”来判定是否相同的时候,会返回false。同理,i3==i4 判定语句也会返回false。
- 不过,上面的分析还是不对,答案并非是两个 false,而是一个 true,一个 false。
Integer 享元模式
- 当我们通过自动装箱,也就是调用 valueOf() 来创建 Integer 对象的时候,如果要创建的 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 只缓存-128到127之间的整型值呢?
- 在 IntegerCache 的代码实现中,当这个类被加载的时候,缓存的享元对象会被集中一次性创建好。
- 毕竟整型值太多了,我们不可能在 IntegerCache 类中预先创建好所有的整型值,这样既占用太多内存,也使得加载 IntegerCache 类的时间过长。所以,我们只能选择缓存对于大部分应用来说最常用的整型值,也就是一个字节的大小(-128到127之间的数据)。
- 实际上,JDK 也提供了方法来让我们可以自定义缓存的最大值。。不过,这里注意一下,JDK 并没有提供设置最小值的方法。
代码片段再次分析
- 因为56处于-128和127之间,i1 和 i2 会指向相同的享元对象,所以 i1==i2 返回 true。
- 而129大于127,并不会被缓存,每次都会创建一个全新的对象,也就是说,i3 和 i4 指向不同的 Integer 对象,所以 i3==i4 返回 false。
- 实际上,除了 Integer 类型之外,其他包装器类型,比如 Long、Short、Byte 等,也都利用了享元模式来缓存-128到127之间的数据。
- 对于下面这样三种创建整型对象的方式,我们优先使用后两种:
- 第一种创建方式并不会使用到 IntegerCache
- 而后面两种创建方法可以利用 IntegerCache 缓存,返回共享的对象,以达到节省内存的目的
Integer a = new Integer(123);Integer a = 123;Integer a = Integer.valueOf(123);
享元模式在Java String中的应用
String s1 = "小争哥";String s2 = "小争哥";String s3 = new String("小争哥");System.out.println(s1 == s2);System.out.println(s1 == s3);
- 上面代码的运行结果是:一个true,一个false。
- 跟 Integer 类的设计思路相似,String 类利用享元模式来复用相同的字符串常量(也就是代码中的“小争哥”)。JVM 会专门开辟一块存储区来存储字符串常量,这块存储区叫作“字符串常量池”。
- 不过,String 类的享元模式的设计,跟 Integer 类稍微有些不同:
- Integer 类中要共享的对象,是在类加载的时候,就集中一次性创建好的。
- 但是,对于字符串来说,我们没法事先知道要共享哪些字符串常量,所以没办法事先创建好,只能在某个字符串常量第一次被用到的时候,存储到常量池中,当之后再用到的时候,直接引用常量池中已经存在的即可,就不需要再重新创建了。
转载地址:https://blog.csdn.net/linglongwunv/article/details/117951428 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
不错!
[***.144.177.141]2024年08月20日 22时14分03秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
最新版kubeadm快速部署Kubernetes
2019-05-24
锁使用的一些笔记
2019-05-24
Linux SVA特性分析
2019-05-24
使用SMMU的PMU查看性能数据
2019-05-24
Linux文件引用计数的逻辑
2019-05-24
SMMU SSV的逻辑
2019-05-24
Linux内核DMA子系统分析
2019-05-24
使用ftrace跟踪函数
2019-05-24
Linux dma_map_sg API
2019-05-24
Intel QAT ZIP初步分析
2019-05-24
Linux kernel PCI is_physfn的逻辑
2019-05-24
Dump PCIe设备BAR寄存器
2019-05-24
使用linux zswap
2019-05-24
Linux zswap构架分析
2019-05-24
Linux workqueue分析
2019-05-24
Unix/Linux编程实践教程笔记(11-15)
2019-05-24
hisi perf uncore event介绍
2019-05-24
Linux thermal子系统和lm_sensors用户态工具
2019-05-24
vSVA逻辑分析
2019-05-24