swap()函数为什么不能交换Integer类型的数值引发的一系列问题及思考

先来看下面的场景:

要求写一个函数交换Integer类型的a和b的值:

注意这是错误的版本

	public static void swap(Integer a,Integer b) {
        Integer t=a;
        a=b;
        b=t;
    }
    public static void main1(String[] args) {
        Integer x = 1;
        Integer y = 2;
        System.out.println("交换前:" + x + ":" + y);
        swap(x,y);

        System.out.println("交换后:" + x + ":" + y);
    }

在这里插入图片描述
很不幸,这是错误的方法。那么为什么Integer时引用类型的还是没有交换成功呢?

  • 我们先来看内存图:
    在这里插入图片描述通过上图我们可以看到x,y在堆里的指向始终没有改变。去查看Integer内部的源码:
public final class Integer extends Number implements Comparable<Integer> {
  
我们可以看到Integer使用final类型的int进行存储的。final类型的
变量不能被重新赋值,所以操作参数传递变量时,实际上是操作的变量对
象的副本(java中的包装类型都是默认使用这种方式实现的,使用拷贝副
本的方式提升效率和减少内存消耗)。

所以swap中传递的a,b只是传递的x,y值的副本,对副本进行操作不会影响原来的值.
再看第二种错误方法:

	public static void main(String[] args) {
        User a = new User("1");
        User b = new User("2");
        System.out.println("交换前name:" + a + ":" + b);
        swap(a,b);

        System.out.println("交换后name:" + a + ":" + b);
    }

    private static void swap(User a, User b) {
        User tmp = a;
        a = b;
        b = tmp;
    }

}
class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

在这里插入图片描述我们发现还是交换错误的。它实际上并没有修改堆里面的对象,
所以我们看他到底交换没主要是看他修改的是变量(引用)还是修改的堆里面的对象

那么我们该怎么交换两个变量的值呢?

我们可以用反射修改两个变量的值

public static void swap4(Integer a, Integer b) throws Exception {
    Field field = Integer.class.getDeclaredField("value");
    field.setAccessible(true);//设置可以访问成员的私有不可变的变量
    Integer tmp =new Integer(a.intValue());
    field.set(a, b.intValue());
    field.set(b, tmp);
}

java到底是值传递还是引用传递?

我们知道java有基本数据类型(8种)和引用数据(类,接口,String,数组…)类型:
在这里插入图片描述

  • 基本数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,
  • 引用类型继承于Object类(也是引用类型)都是按照Java里面存储对象的内存模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的;

而我们java中还是值传递,如果参数是基本类型,传递的是基本类型的变量值。如果参数是引用类型,传递的是该参数所引用的对象在堆中地址值。而地址值也是值,

  • 值传递:就是在方法调用的时候,实参是将自己的一份拷贝赋给形参,在方法内,对该参数值的修改不影响原来实参。
  • 引用传递:是在方法调用的时候,实参将自己的地址传递给形参,此时方法内对该参数值的改变,就是对该实参的实际操作。”

猜你喜欢

转载自blog.csdn.net/qq_41552331/article/details/105843059
今日推荐