Java值传递与引用传递

说起Java的传参,大家肯定不陌生。

那么下面我们来说说值传递与引用传递。

值传递

什么是值传递?首先我们来看一个例子

package com.noesblog;

public class ValueTest {

    public static void main(String[] args) {
        ValueTest valueTest = new ValueTest();
        valueTest.v();
    }

    public void v(){
        int i = 0;
        System.out.println(i);
        v1(i);
        System.out.println(i);
    }

    public void v1(int i){
        i = 10;
        System.out.println(i);
    }
}

现在,请你猜测一下,在控制台打印的字符及顺序是?

答案是这样的:

0
10
0

为什么呢?因为在执行

v1(i);

这一行代码时,JVM虚拟机是将i这个变量的值拷贝了一份,然后将这个值的地址传递过去,因此,在v1函数中执行的

        i = 10;

这一步,仅仅是修改了新的地址中由0 -> 10的值变化。而在原有方法v()中,i的值并没有改变,因此在执行v1()方法后,i的值依然是0

引用传递?

现在,我们创建一个User类

package com.noesblog;

public class User {
    private String userName;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return super.toString()+"User{" +
                "userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

在User类中,有两个成员属性,分别是userName与password,并且还包含了相应的get/set方法以及重写了toString方法。
下面,我们修改ValueTest这个类的的代码,变成如下的代码。

package com.noesblog;

public class ValueTest {

    public static void main(String[] args) {
        ValueTest valueTest = new ValueTest();
        valueTest.t();
    }

    public void t(){
        User user = new User();
        user.setUserName("li");
        user.setPassword("123");
        System.out.println(user);
        t1(user);
        System.out.println(user);
    }

    public void t1(User user){
        user.setUserName("asd");
        user.setPassword("111");
        System.out.println(user);
    }
}

在t方法中,我们创建了一个User对象,并设置了其属性值,以及打印了一次这个对象。

现在,我们来猜猜,user对象在执行t1(user)方法之后,对象中的值有没有发生变化呢?

打印结果如下:

com.noesblog.User@1540e19dUser{userName='li', password='123'}
com.noesblog.User@1540e19dUser{userName='asd', password='111'}
com.noesblog.User@1540e19dUser{userName='asd', password='111'}

由于我们在toString方法中执行了super.toString方法,因此会打印对象的地址值,可以看出对象的引用并没有发生变化,而对象中的值却发生了变化。

但是这并不属于引用传递,因为实际运行过程中,依旧是将user这个引用进行拷贝,然后传递拷贝后的变量,由于两个引用均指向同一个对象,因此对于这个对象的操作仍旧有效。

我们继续看一个例子,首先定义一个Order类

package com.noesblog.basis;

public class Order {

    private int orderId;
    private String orderName;

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }


    public Order(int orderId, String orderName) {
        this.orderId = orderId;
        this.orderName = orderName;
    }

    @Override
    public String toString() {
        return super.toString() + "Order{" +
                "orderId=" + orderId +
                ", orderName='" + orderName + '\'' +
                '}';
    }
}

然后定义一个方法,用于交换两个Order对象

    private void orderSwap(Order order1,Order order2){
        Order temp = order1;
        order1 = order2;
        order2 = temp;
    }

接着我们执行一个测试

    @Test
    public void orderTest(){
        Order order1 = new Order(1,"A");
        Order order2 = new Order(2,"B");
        orderSwap(order1,order2);
        System.out.println("order1" + order1);
        System.out.println("order2" + order2);
    }

首先我们新建了两个order对象,然后执行了orderSwap方法,接着打印了这两个对象,按照之前的思路来说,order1与order2对象中的值应该发生了交换。
而实际打印的结果是,两者值仍旧不变!
因为在执行orderSwap(order1,order2);这行代码时,虚拟机copy了order1与order2的引用,然后将copy后的内容传递给方法,因此在orderSwap方法中实际修改的仅仅是orderSwap的order1变量所指向的引用,实际内存图如下
order1 -copy-> xx1 = {1,”A”}
order2 -copy-> xx2 = {2,”B”}

orderSwap(xx1,xx2) //执行该行后
xx1 -> order1 变更为xx1 -> order2
xx2 -> order2 变更为xx2 -> order1
order1 -> {1,”A”} 仍旧保持
order2 -> {2,”B”} 仍旧保持

总结

因此,在Java中,实际并不存在引用传递这一说法,所有的方法参数传递均为值传递。
注意这里需要区分是基本类型的值传递与引用类型的值传递是一样的,都是copy参数值然后传递,不同的是对象引用是可以拿到实际的对象引用,因此对对象的内部操作是完全有效的,而对对象引用的操作(例如上面这个例子),则仅在方法域变更对象引用有效,方法执行完毕后原order1和order2保持不变。

发表评论

电子邮件地址不会被公开。 必填项已用*标注