ByteShortIntegerLong内部缓存类的对比与源码分析
Posted 然笑后端
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ByteShortIntegerLong内部缓存类的对比与源码分析相关的知识,希望对你有一定的参考价值。
对于基本数据类型的包装类 Byte
、Short
、Integer
、Long
,其内部实现都有一个缓存类,这个缓存类主要用于缓存固定区间的数值对象,默认为 [-128, 127],其中 Integer
的缓存区间最大值可以通过属性动态配置,而 Byte
、Short
、Long
则不能动态配置。
在平时的开发过程中,可能对于这些包装类的内部缓存没有过多的关注,如果没有阅读过相关源码,也可能完全不知道还有内部缓存类的存在,但是下面的代码所展示的或许见过。
public static void main(String[] args)
Integer num1 = 100;
Integer num2 = 100;
System.out.println("num1 与 num2 是否相等:" + (num1 == num2));
Integer num3 = 128;
Integer num4 = 128;
System.out.println("num3 与 num4 是否相等:" + (num3 == num4));
运行上面的代码,输出结果是什么呢?了解过 Integer
内部缓存池的可能会给出如下的结果
num1 与 num2 是否相等:true
num3 与 num4 是否相等:false
但是在此处,我想说的是,这个结果不完全正确,为什么呢?因为漏掉了另外一种可能的情况,准确来说,上面的程序运行可能会出现两种结果:
-
未改变
Integer
缓存区间最大值时,默认缓存区间为[-128, 127]
,此时上面程序输出结果如下num1 与 num2 是否相等:true num3 与 num4 是否相等:false
-
通过
java.lang.Integer.IntegerCache.high
属性改变了Integer
缓存区间的最大值时,比如改变后的缓存区间为[-128, 200]
,那么此时上面程序的输出结果就不一样了,如下num1 与 num2 是否相等:true num3 与 num4 是否相等:true
为什么会这样呢?上面的程序定义涉及到 int
与 Integer
之间的装箱操作,而装箱操作是在编译时自动完成的,具体装箱操作调用的是 Integer
的 valueOf()
方法,而 valueOf()
方法的实现又与 Integer
的内部缓存类 IntegerCache
的实现有关,IntegerCache
的缓存区间最大值是可以动态配置的。
那么对于 Byte
、Short
、Long
是否也是一样的呢?答案是否定的,也就是说,如果上面程序的类型改成 Byte
、Short
、Long
任意一种,其输出结果只会有一种,也就是对应 Integer
的第一种结果,下面来具体分析各个类的内部缓存类的源码实现,在分析完后,或许就有了不一样的理解和认识。
1、Byte 的内部缓存类 ByteCache
private static class ByteCache
private ByteCache()
// 定义了一个大小为 256 的 Byte[] 数组缓存
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
// 静态代码块用于初始化 Byte[] 数组缓存
static
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
ByteCache
为 Byte
的内部静态类,由于构造函数为私有的,所以不能被实例化,缓存类中只有一个成员变量 cache
,它是一个大小为 256 的 Byte
数组,静态代码块初始化时,会向数组中填充 256个 Byte
对象,这些 Byte
对象对应的数值是 [-128, 127],下面再来看看 Byte
类 valueOf()
方法的实现。
public static Byte valueOf(byte b)
// 偏移量为 128
final int offset = 128;
// 这里直接从 ByteCache 的缓存数组中获取 Byte 对象
return ByteCache.cache[(int)b + offset];
public static Byte valueOf(String s, int radix)
throws NumberFormatException
// 调用的还是上面的方法
return valueOf(parseByte(s, radix));
public static Byte valueOf(String s) throws NumberFormatException
return valueOf(s, 10);
虽然 Byte
类提供了3个 valueOf()
方法,但是通过分析可知,最终调用的都是第一个方法,即 valueOf(byte b)
这个,由于 byte 能表示的数值区间为 [-128, 127]
,所以如果是直接通过同一数值定义多个 Byte
,这些 Byte
实例对象都是相同的,如下:
public static void main(String[] args)
Byte byte1 = 1;
Byte byte2 = 1;
Byte byte3 = 1;
System.out.println(byte1 == byte2);
System.out.println(byte2 == byte3);
输出的结果如下:
true
true
那么如果换一种方式定义 byte1
、byte2
、byte3
,结果又会怎么样呢?
public static void main(String[] args)
Byte byte1 = new Byte("1");
Byte byte2 = new Byte("1");
Byte byte3 = new Byte("1");
System.out.println(byte1 == byte2);
System.out.println(byte2 == byte3);
运行程序输出结果如下:
false
false
因为当直接使用 new
关键字创建对象时,会直接在堆上分配新的内存空间,同时 new Byte("1")
的实现和 valueOf()
并不一样,所以 创建的 byte1
、byte2
、byte3
对象并不相同。
2、Short 的内部缓存类 ShortCache
private static class ShortCache
private ShortCache()
// 定义了一个大小为 256 的 Short[] 数组缓存
static final Short cache[] = new Short[-(-128) + 127 + 1];
static
for(int i = 0; i < cache.length; i++)
cache[i] = new Short((short)(i - 128));
ShortCache
缓存类和 ByteCache
缓存类很类似,缓存数值的大小都是 256,缓存数值区间都是 [-128, 127]
,只是数值的类型不一样,下面再来看看 Short
类的 valueOf()
方法实现。
public static Short valueOf(short s)
final int offset = 128;
int sAsInt = s;
// 如果 s 值在 [-128, 127] 之间,则直接返回 ShortCache.cache[] 中已缓存的对象
if (sAsInt >= -128 && sAsInt <= 127) // must cache
return ShortCache.cache[sAsInt + offset];
// 不在 [-128, 127] 之间时,直接使用 new Short() 创建对象
return new Short(s);
public static Short valueOf(String s) throws NumberFormatException
return valueOf(s, 10);
public static Short valueOf(String s, int radix)
throws NumberFormatException
return valueOf(parseShort(s, radix));
和 Byte
类一样,Short
类也有3个 valueOf()
方法,只是 Short
类的 valueOf()
方法实现却不相同,主要由于 Short
类能表示的数值区间为 [-32768, 32767]
,而 ShortCache
缓存的数值区间只有 [-128, 127]
,所以只有当数值在 [-128, 127]
之间时,才能从缓存中复用对象,超过这些数值的,缓存中也没有。
3、Long 的内部缓存类 LongCache
private static class LongCache
private LongCache()
// 定义了一个大小为 256 的 Long[] 数组缓存
static final Long cache[] = new Long[-(-128) + 127 + 1];
static
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
LongCache
的实现和 ShortCache
、ByteCache
并无区别,只是类型不一样而已,下面来看看其 valeuOf()
的实现
public static Long valueOf(long l)
final int offset = 128;
// 如果 l 值在 [-128, 127] 之间,则直接返回 LongCache.cache[] 中已缓存的对象
if (l >= -128 && l <= 127) // will cache
return LongCache.cache[(int)l + offset];
return new Long(l);
public static Long valueOf(String s) throws NumberFormatException
return Long.valueOf(parseLong(s, 10));
public static Long valueOf(String s, int radix) throws NumberFormatException
return Long.valueOf(parseLong(s, radix));
可以看出,Long
类的 valueOf()
方法和 Short
类的 valueOf()
方法是一样的。
4、Integer 的内部缓存类 IntegerCache
之所以把 IntegerCache
放在最后分析,是因为 IntegerCache
的实现和其他的都不太一样,具体源码如下:
private static class IntegerCache
// 最小值 -128
static final int low = -128;
// 最大值
static final int high;
// 缓存数组
static final Integer cache[];
static
// 最大值默认为 127,也可以通过属性字段配置,具体属性名为 java.lang.Integer.IntegerCache.high
int h = 127;
// 获取 java.lang.Integer.IntegerCache.high 属性值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
// 如果属性值不为空
if (integerCacheHighPropValue != null)
try
// 使用 parseInt() 方法将属性值转换为 int 类型
int i = parseInt(integerCacheHighPropValue);
// 取属性值与127两者之间的最大值,也就是说 i 的最小值其实是127
i = Math.max(i, 127);
// 数组的最大值为 Integer.MAX_VALUE
// 取 i 和 Integer.MAX_VALUE - (-low) -1 两者之间最小值
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
catch( NumberFormatException nfe)
// If the property cannot be parsed into an int, ignore it.
high = h;
// 创建大小为 (high - low) + 1 的整型数组
cache = new Integer[(high - low) + 1];
int j = low;
// 创建并缓存 Integer 对象
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
// 断言缓存最大值 high 大于等于 127
assert IntegerCache.high >= 127;
private IntegerCache()
结合上面已经分析的 ByteCache
和 ShortCache
,可以看出 IntegerCache
缓存类是有明显区别的,实现代码更多,从 IntegerCache
的定义可以看出,3个成员变量都是静态常量,只有一个静态代码块,没有任何其他的方法,关于静态代码块的分析已经在代码中备注了,从静态代码块中可以得出以下的几点:
IntegerCache
的缓存最小值low
为 -128,与其他的类Byte
、Short
、Long
是相同的;IntegerCache
的缓存最大值high
可以通过属性java.lang.Integer.IntegerCache.high
进行设置,而其他的类Byte
、Short
、Long
都是无法设置的;IntegerCache
的缓存最大值high
的最小值为127,如果通过属性设置的值比127小,则赋值为 127,也就是此时的属性值是无效的;
上面提到 IntegerCache
的缓存最大值 high
可以通过属性 java.lang.Integer.IntegerCache.high
进行设置,那么又该如何配置呢?这里以 Intellij IDEA
进行举例说明,首先找到测试类的配置,然后在 VM Options
参数一栏添加 -Djava.lang.Integer.IntegerCache.high=200
即可,如下:
::: hljs-center
:::
此处将 java.lang.Integer.IntegerCache.high
设置为 200,再次运行下面的程序
public static void main(String[] args)
Integer num1 = 100;
Integer num2 = 100;
System.out.println("num1 与 num2 是否相等:" + (num1 == num2));
Integer num3 = 128;
Integer num4 = 128;
System.out.println("num3 与 num4 是否相等:" + (num3 == num4));
输出的结果如下:
num1 与 num2 是否相等:true
num3 与 num4 是否相等:true
重要提示:
5、Float 与 Double
分析到此处,可能有人会问,基本的数据类型中还有 float
和 double
类型,它们也有包装类 Float
和 Double
,是不是它们也有内部缓存类呢?答案是否定的, Float
和 Double
类中是没有内部缓存类的,也就是说不会存在对象复用,比如下面的代码:
public static void main(String[] args)
Float num1 = 1F;
Float num2 = 1F;
System.out.println(num1 == num2);
Double num3 = 1D;
Double num4 = 1D;
System.out.println(num3 == num4);
运行程序,输出结果如下:
false
false
以上是关于ByteShortIntegerLong内部缓存类的对比与源码分析的主要内容,如果未能解决你的问题,请参考以下文章