如何写一个方法交换两个Integer类型的值?

0.上节我们将String类型的a和b在方法调用中改值,这节我们要在方法调用中将两个int 类型的a和b的值交换

众所周知,a和b是基本数据类型,会在一顿操作猛如虎之后被弹栈,不会对main中的值造成任何改变,但是今天还是来挑战下自我,但是考虑到基本数据类型值存储在栈中,引用数据类型值存储在堆中,所以计划利用int的包装类Integer来实现

1.如果是2个int类型的数值,是无法进行值交换的,因为基本数据类型就是一个数值,无法向引用数据类型一样传入其他方法进行"偷梁换柱"

    public static void main(String[] args) throws Exception {
        int a = 1;
        int b = 2;
        swap0(a, b);//无用
        System.out.println(a);//1
        System.out.println(b);//1
    }

2.可以用反射的方式,修改Integer的value字段,看样子是成功了

问题1:Integer是final类,咋会说改就改??

-- 我们知道 "final修饰的类不能被继承, final修饰的方法不能被重写,final修饰的字段不能被修改",引用类型记录的是地址值,Integer   a 和 b 指向的地址值从头到尾确实没变,但是那个地址下的值在经过反射后被换掉了,所以这里没问题

问题2:swap1(int a,int b)相当于只将基本数据类型(相当于一个数值)传入了一个方法进行操作,应该是对main中的a和b的值毫无影响的,但是现在结果却出乎意料,究竟是人性的扭曲还是道德的沦丧?

-- 这里涉及到Java中Integer$IntegerCache对Integer值的缓存和优化问题,反射过程实质上会修改缓存中相应值对应的地址处的值

 

其中的原委,且听我娓娓道来...

1)main中地址值 a = {Integer@527},a = {Integer@528},

2)Scala中,内部类属于实例的,而Java中,内部类是属于类的,所以此时,我从a中取得的Integer$IntegerCache.cache[]就是这里所有Integer实例所共享的Integer内存缓存,缓存范围为-128~127

Debug查看Integer$IntegerCache的cache[]中对应的Integer@527= 1 , Integer@528= 2

3)经过field.set(a, newA); field.set(b, newB);时, a,b被自动装箱为Integer,Integer$IntegerCache中数值1对应的地址值被反射设值为100,数值2对应的地址值被反射设值为200

4)返回到main中,a和b对应的地址值未变,但是地址值对应的数值却变化了

3.总结上面的经验,我们利用反射,将a和b进行互换赋值就OK啦?? 但是发现结果是a=2,b=2

分析:

1)首先在 field.set(a, newA); field.set(b, newB);时,a、b、newA、newB、被自动装箱,

2) 执行 field.set(a, newA)前a=1,、newA=2 ,执行时获取1在IntegerCache.cache[]地址值,将该位置的值替换为2

3)field.set(b, newB)时,debug中newB=1,这是IDE显示的一个bug,实际上newB引用的是a的地址值,a处地址值此时对应的值是2,所以这步执行完后,b的值依然是2.换句话说,此时a和b指向的是同一个地址值的数值

那要怎么解决同一个地址值指向的这个问题??

4.既然在缓存范围之内(-128~127)存在这个问题,那如果a和b都不在这个范围之内,是不是就不存在指向同一地址值的问题了?

分析:发现a和b的数值根本没有交换, 走debug瞧一瞧

发现a和b的地址值分别是527和528,不在cache[]数组地址值256~798范围中

进入field.set(b, newB)发现2个全新的地址值

原因是在同一类加载器中,value数值处于(-128~127)范围的Integer在包装时,会直接将地址值指向IntegerCache中的缓存对象,超出范围的会每次都开辟一片新空间new一个Integer实例出来,所以main()中对1000、2000的包装地址和swap3()中对1000、2000的包装地址指向不同,因此在swap3()中进行a和b的反射改值操作对main中的a、b数值毫无影响!

那要怎样解决(-128~127)范围外无法调换数值的问题??

--将swap3(int a, int b)改为swap3(Integer a, Integer b),让main中a和b的地址值传递下来

field.set(a, newA);

 

field.set(b, newB);

可见,

newA和newB都没有直接指向缓存中的地址,而是new Integer 新开辟空间,因此不存在指向同一地址值的问题;

swap3中a和b类型由int换成Integer类型后也与main()中的a和b指向了统一地址值,因此在(-128~127)范围外可以顺利反射改变main()中的a、b值!

 

5.那么在(-128~127)范围内的地址值指向同处的问题该如何解决??

把newA和newB新开辟空间,在field.set(a, newA); field.set(b, newB);时就不会产生b指向a存储地址的问题

发现结果竟然还是 a=2,b=2

看了 valueOf源码才知道,这个方法有缓存先去缓存中拿值了

所以我们不能用这个方法,我们自己new Integer(x)

ok了,完美解决

重点圈起来

 

WoW~~~  夜晚是猿猿们的天堂,但也要注意身体,猿兄们 Good Night !

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值