Java中基本类型和包装类的各类比较(==),以及包装类的对象缓存池

34 篇文章 2 订阅

Java中的基本类型及其包装类的比较(==)一直是一个比较头疼的问题,不仅有自动装箱和拆箱操作,部分的包装类还有对象缓存池,这就导致了这部分知识容易混淆。

对于==操作符来说,如果比较的数据是基本类型,则比较它们的,如果比较的是对象,则会比较对象的内存地址。另外,如果一个是基本类型、一个是包装类型,在比较前会先把包装类型拆箱成基本类型,然后进行比较。

以int为例,这里我们把参与比较的类型分为三种:int、直接new出来的Integer对象自动装箱出来的Integer对象

这里先不考虑Integer的缓存池,我们对三种类型之间的两两比较进行下排列组合,3 * 3一共有9种可能。由于==运算符是不区分左右的先后顺序的,也就是说a == bb == a等价,所以可以去除3种重复比较,这样就只有6种情况了。具体如下图:

类型/类型intnew IntegerInteger AutoBoxing
int
new Integer
Integer AutoBoxing

接下来我们依次看下这六种情况,下面上代码:

情况①:

// ①int 与 int 类型比较
int i1 = 128;
int i2 = 128;
System.out.println("int == int, result:" + (i1 == i2));// true
System.out.println("----------------------------------------------------");

基本类型使用==比较的是,所以这个毫无疑问就是true

情况②:

// ②int类型 和 new出来的Integer对象 比较
int i3 = 128;
Integer i4 = new Integer(128);
// 在进行运算前,i4会自动拆箱,实质是两个int类型比较
System.out.println("int == new Integer, result:" + (i3 == i4));// true
System.out.println("----------------------------------------------------");

基本类型包装类型比较,包装类会先自动拆箱,然后再比较,实质上还是两个int在比较,所以这里结果还是true。

情况③:

// ③int类型 和 自动装箱的Integer对象 比较
int i5 = 128;
Integer i6 = 128;
// 在进行运算前,i6会自动拆箱,实质是两个int类型比较
System.out.println("int == Integer autoBoxing, result:" + (i5 == i6));// true
System.out.println("----------------------------------------------------");

这里的i6在赋值的时候,会自动把128装箱成Integer类型,接着使用==比较的时候,i6又会自动拆箱成int,所以实质上还是两个int在比较,跟情况②原理一样。

情况④:

// ④两个new出来的Integer对象的内存地址比较
Integer i7 = new Integer(128);
Integer i8 = new Integer(128);
System.out.println("new Integer == new Integer, result:" + (i7 == i8));// false
System.out.println("----------------------------------------------------");

这里是两个new出来的包装类对象进行比较,这里比较的是他们各自对象的内存地址,由于是两个不同的对象,所以地址值必然不同,结果是false。

情况⑤:

// ⑤自动装箱的Integer类型 和 new出来的Integer对象 比较
Integer i9 = 128;
Integer i10 = new Integer(128);
System.out.println("Integer autoBoxing == new Integer, result:" + (i9 == i10));// false
System.out.println("----------------------------------------------------");

这里的i9在赋值时会将128自动装箱成Integer类型,所以最终是两个不同对象的内存地址比较,结果必然为false。

情况⑥:

// ⑥自动装箱的Integer对象 和 自动装箱的Integer对象 比较,区间[-128, 127]之外
Integer i11 = 128;
Integer i12 = 128;
System.out.println("Integer autoBoxing == Integer autoBoxing, 区间[-128, 127]之外,result:" + (i11 == i12));// false
System.out.println("----------------------------------------------------");

这里的i10和i11在赋值时都会先将128自动装箱成Integer类型,然后再进行比较,原理跟情况⑤一样,所以最终结果为false。

细心的同学可能已经发现,我这里举例用的数字是128,选择这个数字的意义在于在比较时不涉及Integer的缓存池

Integer的缓存池

关于Integer的缓存池,我们还是举例说明:

// 自动装箱会从缓存池中取对象,缓存池的区间为[-128, 127]
// 自动装箱的Integer对象 和 自动装箱的Integer对象 比较,区间[-128, 127]中
Integer i13 = 127;// 缓存池
Integer i14 = 127;// 缓存池
System.out.println("Integer autoBoxing == Integer autoBoxing, 区间[-128, 127]中,result:" + (i13 == i14));// true

这个例子跟上面的情况⑥一模一样,唯一不同是这里使用的是127情况⑥使用的是128,但是情况⑥的结果是false,这里的比较结果却是true,这就很让人疑惑。

我们分析下这个过程,i13i14的赋值过程中,均会触发自动装箱,给127生成对应的Integer对象;接下来使用==比较这两个Integer对象的地址,最后的输出结果为true

我们知道,Java中如果两个对象的内存地址一样,那么他们就是同一个对象,分配的就是同一块内存。我们这里的i13i14都是自动装箱产生的,是不是在产生过程中返回了同一个对象?

对自动装箱和拆箱有疑问的同学可以看一文带你理解Java中自动装箱和拆箱

Integer对应的自动装箱的方法是Integer#valueOf(),我们看下它的实现:

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

可以看到如果i的值在某个范围内的话,就会从IntegerCache.cache这个数组中取出一个Integer对象返回;如果超出了这个范围的话就直接new一个Integer对象返回。

我们看下IntegerCache的代码:

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;
        ...
        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() {}
}

按照惯例,省略掉了无关代码。

这里的下限low的值默认为-128上限high的值默认为127cache是一个Integer类型的数组

static代码块里面主要做了两件事:
①初始化cache这个Integer数组,数组容量为256。
②给cache的所有元素赋值。在for循环里面,依次给cache的各个元素进行赋值,Integer的值从-128开始,一直到127,对应的是闭区间[-128, 127],刚好256个元素。

所以我们常说的Integer的缓存池其实就是这里的IntegerCache.cache,它在Integer进行类加载的时候初始化。

在基本类型int需要自动装箱的时候,就会调用Integer#valueOf方法,在Integer#valueOf方法的实现中,如果int的值在IntegerCache.cache缓存的范围内(闭区间[-128, 127]),就返回已经构建好的Integer对象,具体是根据角标IntegerCache.cache这个数组中去取。

经过上面的讲解可以知道,我们这里的127经过自动装箱后,是从缓存池中拿的Integer对象,所以i13i14拿到的是同一个Integer对象(因为它们的值相同,在IntegerCache.cache数组中对应的index相同,所以获取到的是同一个缓存对象)。

Byte、Short、Integer、Long、Character、Boolean对应的缓存池。

既然Integer有缓存池,那其他的包装类是否也有同样的情况呢?

我们看下测试代码:

// 自动装箱的Byte对象 和 自动装箱的Byte对象 比较,区间[-128, 127]中
Byte b1 = 127;// 缓存池
Byte b2 = 127;// 缓存池
System.out.println("Byte autoBoxing == Byte autoBoxing, 区间[-128, 127]中,result:" + (b1 == b2));// true
System.out.println("----------------------------------------------------");

// 自动装箱的Short对象 和 自动装箱的Short对象 比较,区间[-128, 127]中
Short s1 = 127;// 缓存池
Short s2 = 127;// 缓存池
System.out.println("Short autoBoxing == Short autoBoxing, 区间[-128, 127]中,result:" + (s1 == s2));// true
System.out.println("----------------------------------------------------");

// 自动装箱的Long对象 和 自动装箱的Long对象 比较,区间[-128, 127]中
Long l1 = 127L;// 缓存池
Long l2 = 127L;// 缓存池
System.out.println("Long autoBoxing == Long autoBoxing, 区间[-128, 127]中,result:" + (l1 == l2));// true
System.out.println("----------------------------------------------------");

// 自动装箱的Character对象 和 自动装箱的Character对象 比较,区间[-128, 127]中
Character c1 = 127;// 缓存池
Character c2 = 127;// 缓存池
System.out.println("Character autoBoxing == Character autoBoxing, 区间[-128, 127]中,result:" + (c1 == c2));// true
System.out.println("----------------------------------------------------");

// 自动装箱的Boolean对象 和 自动装箱的Boolean对象 比较,区间[-128, 127]中
Boolean bool1 = true;// 缓存池
Boolean bool2 = true;// 缓存池
System.out.println("Boolean autoBoxing == Boolean autoBoxing, 区间[true, false]中,result:" + (bool1 == bool2));// true
System.out.println("----------------------------------------------------");

基本类型对应的包装类中,ByteShortIntegerLongCharacter的缓存池都是[-128, 127]Boolean的缓存池比较特殊,只有true和false两个Boolean对象。

总结如下:

基本类型包装类缓存池
byteByte[-128, 127]
shortShort[-128, 127]
intInteger[-128, 127]
longLong[-128, 127]
charCharacter[-128, 127]
booleanBoolean[true, false]
floatFloat
doubleDouble
参考

一文带你理解Java中自动装箱和拆箱

  • 12
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
好的,我会尽力回答你的问题。 1. String和int类型的相互转换: 可以通过以下两种方式将String类型转换为int类型: - 使用Integer的parseInt方法 ```java String str = "123"; int num = Integer.parseInt(str); System.out.println(num); // 输出:123 ``` - 使用Integer的valueOf方法 ```java String str = "123"; int num = Integer.valueOf(str); System.out.println(num); // 输出:123 ``` 可以通过以下方式将int类型转换为String类型: - 使用String的valueOf方法 ```java int num = 123; String str = String.valueOf(num); System.out.println(str); // 输出:123 ``` 2. 自动装箱和拆箱: 自动装箱是指将基本类型自动转换为对应的包装类,而自动拆箱则是将包装类自动转换为对应的基本类型。例如: ```java int num = 123; Integer integer = num; // 自动装箱 int num2 = integer; // 自动拆箱 ``` 3. Integer的面试题的练习: 下面是一道常见的Integer面试题: ```java Integer a = 127; Integer b = 127; System.out.println(a == b); // 输出:true Integer c = 128; Integer d = 128; System.out.println(c == d); // 输出:false ``` 这是因为Java对于byte、short、int三种类型包装类,如果数值在[-128, 127]之间,则会直接使用缓存对象,而不会重新创建对象。因此,a和b在[-128, 127]之间,它们使用的是同一个对象,所以a==b返回true,而c和d超出了这个范围,它们使用的是不同的对象,所以c==d返回false。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tinyvampirepudge

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值