你的Java功底很深?Integer源码解读让你怀疑人生

点击上方“java患者” 可以订阅哦!

关于Integer的讲解

1

main方法中有a,b两个变量,且为Integer类型。通过调用本类的swap方法将二个值互换,并在main方法中输出结果。

——AA

实例代码如下方:

public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        System.out.println("before swap: a="+a+",b="+b);
        swap(a,b);//这是一个Integer交换值的方法
        System.out.println("after swap: a="+a+",b="+b);
    }

    private static void swap(Integer num1,Integer num2){
        Integer tmp = num1;
        num1 = num2;
        num2 = tmp;
    }

1

调用swap方法是传递是的a,b的引用给num1和num2,方法调用完毕时,a和b指向的地址并未发生改变。

那么我们该如何传递?

1. 我们先分析Integer a = 1;到底做了些什么?我们看一下他的字节码:

第一条可以看出,JVM采用iconst指令将常量1压入栈的栈顶,然后调用Integer.valueOf(1),进行装箱操作。

2. 我们来看Integer内部如何赋值的。

下面我们来看下Integer里面的源代码:

/**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

也就是说,实际上a=1;内部的最终操作说对Integer里面的value进行赋值。那我们在swap方法去改变这个value的值可解决我们的问题,显然,我们可以利用到反射来完成。

下面是进一步代码:

private static void swap(Integer num1,Integer num2){
        try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);
            int tmp = num1;
            field.set(num1,num2);
            field.set(num2,tmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行后的结果为:

before swap: a=1,b=2
after swap: a=2,b=2

3. 为啥是这样这,a确实改过来了,b为啥改不了?

这牵扯到装箱和拆箱,我们再来看下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.low, IntegerCache.high]区间内,从cache数组缓存中取。low值是-128,hight值是127。也就是说,如果值是[-128,127]间是从缓存中取。而cache数组元是不同整数-128到127从小到大排列。如cache[129]=1;如果不在这范围内,就进行new Integer(i)。

我们重点来看这三行代码

int tmp = num1;
field.set(num1,num2);
field.set(num2,tmp);

在field.set(num1,num2)过程中,由于set的二个参数都为Object。第一步先取num2值进行装箱,num2是2,在[-128,127]间,从缓存中取,下标是2+128=130,即cache[130]为2。第二步是赋值,相当于cache[129]=2;

在field.set(num2,tmp);过程中,tmp是值为1,自动装箱从缓存中取,索引为129。cache[129]已是为2了,其赋值相当是cache[130]=2,

所以最终输出结果是a为2,b为2。

最后,我们有一个结论,当我们的值在[-128,127]间从cache缓存中取。所以有一下两种情况:

Integer a = 1;
Integer b = 1;
System.out.println(a==b);//true
Integer a = 128;
Integer b = 128;
System.out.println(a==b);//false,不在[-128,127]内,比较地址。

4. 最终解决方案

方案一:

try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);
            int tmp = num1;
            field.set(num1,num2);
            field.set(num2,new Integer(tmp));
        } catch (Exception e) {
            e.printStackTrace();
        }

此时是以一个新的对象,不会从缓存中取。

方案二:

try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);
            int tmp = num1;
            field.set(num1,num2);
            field.setInt(num2,tmp);
        } catch (Exception e) {
            e.printStackTrace();
        }

不产生装箱操作,直接set一个int类型。就不会从缓存中取。

历史文章

初识Spring Data 全家族(1)

限时下载|500G编程资料:springboot、vue、mybatis源码分析、vue、项目实战、数据结构与算法、C语言等

点击蓝字关注我们

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值