Java基础之基本数据类型封装类的缓存
巨人的肩膀:https://blog.csdn.net/hnjcxy/article/details/123787209
1、Java中基本数据类型byte、short、char、int、long、float、double、boolean有对应的封装类型:Byte、Short、Character、Integer、long、Float、Double,Boolean其中Byte、Short、Character、Integer、Long、Boolean都实现了常量池缓存。Byte、Short、Character、Integer、Long通过静态内部Cache类来实现一定范围(下面列出缓存范围)内的数据缓存,而Boolean通过两个静态常量TRUE、FALSE来实现数据缓存。
- Byte类缓存范围:[-128,127] 共256个值。
- Short类缓存范围:[-128,127] 共256个值。
- Character类缓存范围:[0,127] 共128个值。
- Integer类默认缓存范围:[-128,127] 共256个值,可以通过修改VM参数-XX:AutoBoxCacheMax=200 即可修改缓存上限到200
- Long类缓存范围:[-128,127] 共256个值。
- Boolean类就缓存两个值:true、false。
2、装箱和拆箱过程:
装箱:调用封装类 valueOf(x) 方法;
拆箱:调用封装类的 xxValue() 方法。
3、测试
测试前说明:
==对于基本数据类型和对象的比较含义是不同的。对于基本数据类型:“==” 比较的是基本数据类型的数值;对于对象:“==” 比较的是对象在内存中的内存地址。
byte、short、char类型计算后会自动转成整型类型int。
(1)Byte
static void testByte(){
System.out.println("Byte类型:");
Byte i1 = 127;
Byte i2 = 127;
Byte i3 = 0;
Byte i4 = new Byte("127");
Byte i5 = new Byte("127");
Byte i6 = new Byte("0");
System.out.println("i1=i2 " + (i1 == i2)); //true 因为数值在[-128,127]的范围内,使用常量池缓存中的127
System.out.println("i1=i4 " + (i1 == i4)); //false 因为i1指向常量池Byte对象127,i4指向堆中Byte对象127
System.out.println("i4=i5 " + (i4 == i5)); //false 因为i4和i5是堆中两个对象
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型byte做加法运算,
//计算结果是整型的127,所以最终是基本数据类型的值作比较;(byte、short、char类型计算后会自动转成整型类型int)
System.out.println("127=i5+i6 " + (127 == i5 + i6)); //true
System.out.println("byte计算后,转换类型为:"+ myGetType(i5 + i6)); // int
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型byte做加法运算,计算结果是整型的127;
// i1与基本数据类型127比较的时候也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //true
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型byte做加法运算,计算结果是整型的127;
// i4与基本数据类型127比较的时候也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true
/* Byte类中有缓存静态内部类ByteCache;缓存范围:-128~127,共256(cache.length)个值
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
*/
}
(2)Short
static void testShort(){
System.out.println("Short类型:");
Short i1 = 127;
Short i2 = 127;
Short i3 = 0;
Short i4 = new Short("127");
Short i5 = new Short("127");
Short i6 = new Short("0");
Short i1New = 128;
Short i2New = 128;
System.out.println("i1=i2 " + (i1 == i2)); //true 因为数值在[-128,127]的范围内,使用常量池缓存中的127
System.out.println("i1New=i2New " + (i1New == i2New)); //false 超过缓存范围,创建新对象了
System.out.println("i1=i4 " + (i1 == i4)); //false 因为i1指向常量池对象127,i4指向堆中对象127
System.out.println("i4=i5 " + (i4 == i5)); //false 因为i4和i5是堆中两个对象
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型short做加法运算,
//计算结果是整型的127,所以最终是基本数据类型的值作比较;(byte、short、char类型计算后会自动转成整型类型int)
System.out.println("127=i5+i6 " + (127 == i5 + i6)); //true
System.out.println("short计算后,转换类型为:"+ myGetType(i5 + i6)); // int
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型short做加法运算,计算结果是整型的127;
// i1与基本数据类型127比较的时候也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //true
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型short做加法运算,计算结果是整型的127;
// i4与基本数据类型127比较的时候也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true
/* Short类中有缓存静态内部类ShortCache;缓存范围:-128~127,共256(cache.length)个值
private static class ShortCache {
private ShortCache(){}
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));
}
}
*/
}
(3)Character
static void testChar(){
System.out.println("Character类型:");
Character i1New72 = 72;
Character i1 = 40; //表示字符'('
Character i2 = 40;
Character i3 = 32;
Character i4 = new Character('(');//字符'('的十进制码点值是40
Character i4New72 = new Character('H');//字符'H'的十进制码点值是72
Character i5 = new Character('(');
Character i6 = new Character(' '); //空格的十进制码点值是32
Character i1New = 128;
Character i2New = 128;
Character i1New1 = 127;
Character i2New2 = 127;
System.out.println("i1=i2 " + (i1 == i2)); //true 因为数值在[0,127]的范围内,使用常量池缓存中的40
System.out.println("i1New=i2New " + (i1New == i2New)); //false 超过缓存范围,创建新对象了
System.out.println("i1New1=i2New2 " + (i1New1 == i2New2)); //true 没有超过缓存范围,使用常量池缓存中的值
System.out.println("i1=i4 " + (i1 == i4)); //false 因为i1指向常量池Character对象40,i4指向堆中Character对象40
System.out.println("i4=i5 " + (i4 == i5)); //false 因为i4和i5是堆中两个对象
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型char做加法运算,
//计算结果是整型的72,所以最终是基本数据类型的值作比较;(byte、short、char类型计算后会自动转成整型类型int)
System.out.println("72=i5+i6 " + (72 == i5 + i6) +" 因为:i5+i6= " + (i5 + i6)); //true
System.out.println("char计算后,转换类型为:"+ myGetType(i5 + i6)); // int
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型char做加法运算,计算结果是整型的72;
// i1New72与基本数据类型72比较的时候也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i1New72=i2+i3 " + (i1New72 == i1 + i3)); //true
System.out.println("i4New72=i5+i6 " + (i4New72 == i5 + i6)); //true
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,即拆箱后的基本数据类型char做加法运算,计算结果是整型的72;
// i4New72与基本数据类型72比较的时候也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i4=i5+i6 " + (i4New72 == i5 + i6) +" 因为:i5+i6= " + (i5 + i6)); //true
Character c = '(';
System.out.println("字符‘(’的码点值:" + (int)c); //40
/* Caracter类中有缓存静态内部类CharacterCache;缓存范围:0~127,共128(cache.length)个值
private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}
*/
}
(4)Integer
static void testInt(){
System.out.println("Boolean类型:");
Integer i1 = 127;
Integer i2 = 127;
Integer i3 = 0;
Integer i4 = new Integer(127);
Integer i5 = new Integer(127);
Integer i6 = new Integer(0);
Integer i1New = 128;
Integer i2New = 128;
System.out.println("i1=i2 " + (i1 == i2)); //true 因为数值在[-128,127]的范围内,使用常量池缓存中的127
System.out.println("i1New=i2New " + (i1New == i2New)); //false 超过缓存范围,创建新对象了
System.out.println("i1=i4 " + (i1 == i4)); //false 因为i1指向常量池对象127,i4指向堆中对象127
System.out.println("i4=i5 " + (i4 == i5)); //false 因为i4和i5是堆中两个对象
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本数据类型int,所以最终是基本数据类型的值作比较
System.out.println("127=i5+i6 " + (127 == i5 + i6)); //true
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本数据类型int;i1与基本数据类型比较的时候也会自动拆箱
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //true
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本数据类型int;
//i4与i5+i6的基本数据类型int结果比较时,i4也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true
/* Integer类中有缓存静态内部类IntegerCache;默认缓存范围:-128~127,共256(cache.length)个值;
但是可通过 java.lang.Integer.IntegerCache.high 修改高位值,例如:设置vm参数 -XX:AutoBoxCacheMax=200 即
可修改缓存上限为200
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int 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_VALUE
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;
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() {}
}
*/
}
Idea2021通过配置VM options修改Integer缓存上限,具体配置参考:
(5)Long
static void testLong(){
System.out.println("Long类型:");
Long i1 = 127L;
Long i2 = 127L;
Long i3 = 0L;
Long i4 = new Long(127L);
Long i5 = new Long(127L);
Long i6 = new Long(0);
Long i1New = 128L;
Long i2New = 128L;
System.out.println("i1=i2 " + (i1 == i2)); //true 因为数值在[-128,127]的范围内,使用常量池缓存中的127L
System.out.println("i1New=i2New " + (i1New == i2New)); //false 超过缓存范围,创建新对象了
System.out.println("i1=i4 " + (i1 == i4)); //false 因为i1指向常量池对象127L,i4指向堆中对象127L
System.out.println("i4=i5 " + (i4 == i5)); //false 因为i4和i5是堆中两个对象
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本数据类型long,所以最终是基本数据类型的值作比较
System.out.println("127L=i5+i6 " + (127L == i5 + i6)); //true
//因为 i2+i3 做算数运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本数据类型long;i1与基本数据类型比较的时候也会自动拆箱
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //true
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本数据类型long;
//i4与i5+i6的基本数据类型long结果比较时,i4也会自动拆箱,所以最终是基本数据类型的值作比较
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true
/* long类中有缓存静态内部类ByteCache;缓存范围:-128~127,共256(cache.length)个值
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);
}
}
*/
}
(6)Boolean
static void testBool(){
System.out.println("Boolean类型:");
Boolean i1 = true;
Boolean i2 = true;
Boolean i4 = new Boolean("true");
Boolean i5 = new Boolean("true");
System.out.println("i1=i2 " + (i1 == i2)); //true 因为i1、i2使用常量池缓存中的true
System.out.println("i1=i4 " + (i1 == i4)); //false 因为i1指向常量池对象true,i4指向堆中对象true,所以两个对象地址不同
System.out.println("i4=i5 " + (i4 == i5)); //false 因为i4和i5是堆中两个对象,比较的是两个对象地址
//因为 i5 与常量true比较时,会自动拆箱为基本数据类型boolean,所以最终比较的是值,不是对象地址
System.out.println("true=i5+i6 " + (true == i5)); //true
}
(7)Double 和 Float 没有缓存
static void testDouble(){
System.out.println("Double类型:");
Double i1 = 127.0;
Double i2 = 127.0;
Double i3 = 0.0;
Double i4 = new Double(127.0);
Double i5 = new Double(127.0);
Double i6 = new Double(0);
System.out.println("i1=i2 " + (i1 == i2)); //false 没有缓存,是两个不同的Double类型对象
//因为 i2+i3 做算术运算+时,会自动拆箱再做加法,所以结果是拆箱后的基本类型double数值127.0,i1与基本数据类型比较时,
// 也需要拆箱成基本数据类型,最后比较两个基本数据类型127.0的值是否相等
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //true
System.out.println("i1=i4 " + (i1 == i4)); //false 没有缓存,是两个不同的Double类型对象
System.out.println("i4=i5 " + (i4 == i5)); //false 没有缓存,是两个不同的Double类型对象
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,所以结果是基本数据类型double;最后比较两个基本数据类型127.0的值是否相等
System.out.println("127.0=i5+i6 " + (127.0 == i5 + i6)); //true
System.out.println("Double计算后,类型为:"+ myGetType(i5 + i6)); // int
//因为 i5+i6 做算数运算+时,会自动拆箱再做加法,所以结果是基本数据类型double;i4与i5+i6的基本数据类型结果比较时,
// i4也会自动拆箱成基本数据类型double,最后是比较基本数据类型的值,而不是比较封装类对象地址
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true
}
测试中用到的判断基本数据类型的方法:
//判断类型
public static String myGetType(Object o) {
String type = o.getClass().getSimpleName();
if ("Integer".equals(type)) {
return String.valueOf(Integer.TYPE);
} else if ("Long".equals(type)) {
return String.valueOf(Long.TYPE);
} else if ("Float".equals(type)) {
return String.valueOf(Float.TYPE);
} else if ("Double".equals(type)) {
return String.valueOf(Double.TYPE);
} else if ("Short".equals(type)) {
return String.valueOf(Short.TYPE);
} else if ("Byte".equals(type)) {
return String.valueOf(Byte.TYPE);
} else if ("Character".equals(type)) {
return String.valueOf(Character.TYPE);
} else if ("Boolean".equals(type)) {
return String.valueOf(Boolean.TYPE);
}
return "非基本数据类型!";
}