java值交换【1】

1、起因

如何实现java的值交换,看起来好像很简单,但是一顿操作后,发现结果没有改变。

 public static void main(String[] args) {
        Integer a=1;
        Integer b=2;
        swap(a,b);
        //要求输出a=2 b=1
        System.out.println("a="+a+",b="+b);
    }

    private static void swap(Integer a, Integer b) {
        Integer temp=a;
        a=b;
        b=temp;
    }

在这里插入图片描述

2、java值传递

java是值传递的

  • 对于基本数据类型,方法里只是有一份值的拷贝
  • 如果传递的是对象,则是对象的引用值的拷贝,只对这个拷贝进行操作,不会改变原来的引用关系(但是,如果是改变对象的属性,则会生效)。

2.1 不会改变原来的引用关系

比如说
在这里插入图片描述
虽然在swap()方法里面交换了两个对象,但没有改变原来的引用关系,a与b还是指向原来的object对象:
在这里插入图片描述


甚至改成a=null与b=null,仍然不会有影响。这个就是java的值传递,区别于c或者c++的指针。
在这里插入图片描述
在这里插入图片描述

2.2 改变引用对象的属性,会生效

如果是在swap()方法里,改掉原来对象里面的属性,则会生效。
在这里插入图片描述
在这里插入图片描述

3、引入反射机制修改private final修饰的属性值

  • 回到刚刚的那个问题,由于java是值传递,所以普通交换是无效的,于是就会自然而然的想到, 得去改这个引用指向的对象本身。然后,会发现 在Integer对象里面,有value这个属性
    在这里插入图片描述
  • 如果能改掉这个value值,就可以改变值了。但是这个值被 private final 修饰,但可以尝试通过反射机制,改掉这个对象的修饰符,即将private改为public,然后去尝试改变value的值

试试私有字段是否可以修改?
在这里插入图片描述
这个时候发现,私有字段可以改。


但是final修饰的是否可以改呢? 在这里插入图片描述
通过结果发现,private final String username也可以改。


再试试基本类型能否修改:
在这里插入图片描述
牛逼!! 也可以改。


但是 反射修改不掉static修饰的字段,会报错:java.lang.IllegalAccessException: Can not set
static final java.lang.String field
在这里插入图片描述

  • 顺着这个思路,用反射机制完成值的交换
 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Integer a = 4444;
        Integer b = 5555;
        swap(a, b);
        //要求输出a=5555 b=4444
        System.out.println("a=" + a + ",b=" + b);
    }

    private static void swap(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
        Class bb = b.getClass();
        Field bbb = bb.getDeclaredField("value");
        bbb.setAccessible(Boolean.TRUE);
        bbb.set(b, 4444);

        Class aa = a.getClass();
        Field aaa = aa.getDeclaredField("value");
        aaa.setAccessible(Boolean.TRUE);
        aaa.set(a, 5555);
    }

在这里插入图片描述

  • 事实证明:即使private final 修饰的字段,依然可以通过反射进行修改!!所以,即使一个对象,被private+final修饰,且没有set方法,没有任何可以改变其值的方法,依然可以通过反射改掉。甚至就连号称不可变的String类,也能被反射改掉值!
    在这里插入图片描述
    在这里插入图片描述

4、注意

4.1 正确实现值的交换

 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Integer a = 1;
        Integer b = 2;
        swap(a, b);
        //要求输出a=2 b=1
        System.out.println("a=" + a + ",b=" + b);
    }

    private static void swap(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
        // 是new一个变量,不是简单的Integer temp=2 ,因为Integer里面有缓存,缓存了-127128之间的值	
        Integer temp=new Integer(b);  
        Class bb = b.getClass();
        Field bbb = bb.getDeclaredField("value");
        bbb.setAccessible(Boolean.TRUE);
        bbb.set(b, a);

        Class aa = a.getClass();
        Field aaa = aa.getDeclaredField("value");
        aaa.setAccessible(Boolean.TRUE);
        aaa.set(a, temp);
    }

在这里插入图片描述

4.2 Integer缓存了-127 到128之间的值

  • Integer里面有缓存,缓存了-127 到128之间的值,使用valueOf() 的时候会先返回缓存里存放的值,不满足时,才会新建一个!!
    在这里插入图片描述
  • 普通的赋值,如 Integer temp=2,这个时候取的是缓存的值。

通过反射去改b的值,导致temp也发生了变化,原因如下:
(1)在这里,a和b的引用均指向缓存地址,地址上存储的值分别为1和2。
(2)将把b指向的值,通过反射改为1的时候,实际上是将缓存地址上的值改掉了。原本b引用指向的就是2,而temp=2 也是指向的2,b和temp都是指向了同一个值,将那个值改掉之后,b和temp都会变。
在这里插入图片描述


如果b和temp不是指向同一个在-127和128范围的数,则b改变,temp不改变!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值