Java中值传递和引用传递

  值传递和引用传递问题一般是相对函数而言的,那说到函数就得提到有关参数传递给方法的两个专业术语:按值调用(call by value)和 按引用调用(call by reference)。

  按值调用是说方法接收的是调用者提供的值,而按引用调用则是方法接收的是调用者提供的变量地址(用C语言的话来说就是指针,当然java并没有指针的概念)。

  这里需要特别注意的是一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值,这是按值调用与引用调用的根本区别,如果没理解后面会有例子解释说明。

  其实java中并不存在引用调用,因为java程序设计语言确实是采用了按值调用,即call by value。也就是说方法得到的是所有参数值的一个拷贝,方法并不能修改传递给它的任何参数变量的内容。代码案例:

 1 public class CallByValue {
 2 
 3     private static int Y=5;
 4 
 5     public static void updateValue(int value){
 6         value = 2 * value;
 7     }
 8 
 9     public static void main(String[] args) {
10         System.out.println("调用前Y的值:"+Y);
11         updateValue(Y);
12         System.out.println("调用后Y的值:"+Y);
13     }
14 
15 }
调用前Y的值:5
调用后Y的值:5

  然而发现Y的值并没有改变

分析:

  1. value被初始化为Y值的一个拷贝(也就是5)
  2. value被乘以2后等于10,但注意此时Y的值仍为5
  3. 方法调用结束后,参数变量value不再使用,被回收。

总结:

  当传递方法参数类型为基本数据类型(数字以及布尔值)时,一个方法是不可能修改一个基本数据类型的参数。

  然而在java中除了基本数据类型外还有引用数据类型,接下来看它的传递情况。

//声明一个User类
public class User {
    private String name;
    private int age;
    public User(String name, int age) {
        this.name=name;
        this.age=age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

测试代码:

public class CallByValue {
    private static User user=null;
    public static void updateUser(User student){
        student.setName("YJ");
        student.setAge(20);
    }


    public static void main(String[] args) {
        user = new User("Luzun",18);
        System.out.println("调用前user的值:"+user.toString());
        updateUser(user);
        System.out.println("调用后user的值:"+user.toString());
    }
}
调用前user的值:User [name=Luzun, age=18]
调用后user的值:User [name=YJ, age=20]

  很显然,User的值被改变了,也就是说方法参数类型如果是引用类型的话,引用类型对应的值将会被修改。

分析:

  1. student变量被初始化为user值的拷贝,这里是一个对象的引用。
  2. 调用student变量的set方法作用在这个引用对象上,user和student同时引用的User对象内部值被修改。
  3. 方法结束后,student变量不再使用,被释放,而user还是没有变,依然指向User对象。

总结:

  当传递方法参数类型为引用数据类型时,一个方法将修改一个引用数据类型的参数所指向对象的值。

  通过上面的测试你可能就会觉得java同时拥有按值调用和按引用调用,然而可惜的是这是有误导性的,虽然上面引用传递表面上体现了按引用调用现象,但是java中确实只有按值调用而没有按引用调用。下面再进行一个测试:

public class CallByValue {
    private static User user=null;
    private static User stu=null;

       public static void swap(User x,User y){
        User temp =x;
        x=y;
        y=temp;
    }
public static void main(String[] args) { user = new User("teacher",28); stu = new User("student",16); System.out.println("调用前user的值:"+teacher.toString()); System.out.println("调用前stu的值:"+student.toString()); swap(teacher,student); System.out.println("调用后user的值:"+teacher.toString()); System.out.println("调用后stu的值:"+student.toString()); } }

  可以通过一个swap函数来交换两个变量teacher和student的值,如果是按引用调用那么一个方法可以修改传递引用所对应的变量值,也就是说如果java是按引用调用的话,那么swap方法将能够实现数据的交换,而实际运行结果:

调用前teacher的值:User [name=teacher, age=28]

调用前student的值:User [name=student, age=16]

调用后teacher的值:User [name=teacher, age=28]

调用后student的值:User [name=student, age=16]

  发现teacher和student的值并没有发生变化,也就是方法并没有改变存储在变量teacher和student中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝的值而已,最终在方法结束后x,y被丢弃,而原来的变量teacher和student仍然引用这个方法调用之前所引用的对象。这个过程也充分说明了java程序设计语言对对象采用的不是引用调用,实际上是对象引用进行的是值传递。java函数在传递引用数据类型时,也只是拷贝了引用的值罢了,之所以能修改引用数据是因为它们同时指向了一个对象,但这仍然是按值调用而不是引用调用。

原文地址:https://www.cnblogs.com/jie-y/p/10787172.html