Java中是值传递和引用传递

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

引用传递:是在方法调用的时候,实参将自己的地址传递给形参,此时方法内对该参数值的改变,就是对该实参的实际操作。

Java中只有值传递
首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。

按值调用(call by value):表示方法接收的是调用者提供的值。

按引用调用(call by reference):表示方法接收的是调用者提供的变量地址。

它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。

Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,即,方法不能修改传递给它的任何参数变量的内容。

swap( ) 场景
要求写一个函数交换int类型的a和b的值

在 swap() 方法中,a、b 的值进行交换,并不会影响到 A、B。因为,a、b 中的值,只是从 A、B 复制过来的。也就是说,a、b 相当于 A、B 的副本,副本的内容无论怎么修改,都不会影响到原件本身。

如果我们这么写:

public class test{
  
    public static void main(String[] args) {
          int A = 2;
          int B = 3;
          swap(A, B);
        System.out.println(A);
        System.out.println(B);
    }
    
      public static void swap(int a, int b){
        int tmp = a;
        a = b;
        b = tmp;
        }
}


运行结果为:

2
3


发现A和B的值并没有交换,为什么呢?

因为Java中采用的是值传递,也就是说执行swap(int a, int b)时,这里的参数a和b,只是A和B的副本,函数的运行结果并没有改变原来A和B的值。

那么采用Integer呢?

如果将上面的int类型转变为Integer,swap(Integer a, Integer b)会不会实现交换功能呢?

public class test{
  
    public static void main(String[] args) {
          Integer A = 2;
          Integer B = 3;
          swap(A, B);
        System.out.println(A);
        System.out.println(B);
    }
    
      public static void swap(Integer a, Integer b){
        Integer tmp = a;
        a = b;
        b = tmp;
        }
}


运行结果为:

2
3


可见还是没有完成交换!

去查看Integer的源码:

public final class Integer extends Number implements Comparable<Integer> {}

可以看到Integer使用final修饰的int进行存储。final修饰的变量不能被重新赋值,所以操作参数传递变量时,实际上是操作变量对象的副本(Java中的包装类型都是默认使用这种方式实现的,使用拷贝副本的方式提升效率和减少内存消耗)。

如果换作是数组呢?

public static void main(String[] args) {
    int[] arr = { 1, 2, 3, 4, 5 };
    System.out.println(arr[0]);
    change(arr);
    System.out.println(arr[0]);
}

public static void change(int[] array) {
    // 将数组的第一个元素变为0
    array[0] = 0;
}


运行结果为:

1

0


这里方法array是对象的引用arr的拷贝,而不是对象本身的拷贝,因此, array 和 arr 指向的是同一个数组对象。

如果换做是一般对象呢?

很多程序设计语言(特别是 C++ 和 Pascal)提供了两种参数传递的方式:值调用和引用调用。

有些程序员(甚至本书的作者)认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。

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 class test{
        public static void main(String[] args) {
        User A = new User("ali");
        User B = new User("bd");
        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;
    }
}


运行结果为:

交换前name:ali-->bd
交换后name:ali-->bd


发现还是没有交换!

所以到底有没有交换,主要是看它修改的是变量(引用)还是修改的堆里面的对象。

交换前:


交换后:

 

 


通过上面两张图可以很清晰的看出: 方法并没有改变存储在变量 A 和 B 中的对象引用。swap() 方法的参数 a 和 b 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。

那么到底该如何实现交换两个变量的值呢?

用容器(或者数组)
例如:

public class test{

    public static void main(String[] args) {
        int[] arr = {2, 3};
        int A = arr[0];
        int B = arr[1];
        swap(arr, 0, 1);
        A = arr[0];
        B = arr[1];
        System.out.println(A);
        System.out.println(B);
    }

    public static void swap(int[] arr, int a, int b){
        int tmp = arr[a];
        arr[a] = arr[b];
        arr[b] = tmp;
    }
}


运行结果为:

3
2


用反射


public static void swap(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);
}

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值