为什么说 java 是按值传递

值传递和引用传递

我们这边所说的传递主要发生过程在调用方法的时候实际参数与形式参数之间的传递。

定义

  • 值传递(pass by value):在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

  • 引用传递(pass by reference):指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

区别

传递类型值传递引用传递
本质区别创建副本不会创建副本
导致结果实际参数和形式参数本质互不影响形式参数会影响实际参数

误区

新手上路,难免有一些误区,常见的误区如下:

  • 如果传输的是普通类型则是值传递,传输的对象则是引用传递

  • 值传递和引用传递的区别取决于传递的参数是个值还是引用

这两点都会导致很多 java 从业者认为 java 包含值传递和引用传递。

反例

我们通过一个对象和一个字符类型来举这个反例,代码如下:


package com.dao.netty.test;

import com.alibaba.fastjson.JSON;

/**
 * 传递
 *
 * @author 阿导
 * @CopyRight 万物皆导
 * @Created 2019年02月20日 18:52:00
 */
public class PassDemo {

    public static void main(String[] args) {
        String name = "旭";

        User user = new User();
        user.setName(name);

        System.out.println("[实际参数]改名之前,普通类型 name:"+name);
        System.out.println("[实际参数]改名之前,对象 user:"+ JSON.toJSONString(user));
        changeName(name);
        System.out.println("[实际参数]普通参数改名后,普通类型 name:"+name);
        System.out.println("[实际参数]普通参数改名后,对象 user:"+ JSON.toJSONString(user));
        changeName(user);
        System.out.println("[实际参数]对象参数改名后,普通类型 name:"+name);
        System.out.println("[实际参数]对象参数改名后,对象 user:"+ JSON.toJSONString(user));

    }

    private static void changeName(String name){
        name="阿导";
        System.out.println("[形式参数]对象参数改名后,普通类型 name:"+name);
    }

    private static void changeName(User user){
        user.setName("阿导");
        System.out.println("[形式参数]对象参数改名后,对象 user:"+ JSON.toJSONString(user));
    }
}


class User{
    /**
     * 姓名
     */
     private String name;


    public String getName() {
        return name;
    }

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

看看输出结果


[实际参数]改名之前,普通类型 name:旭
[实际参数]改名之前,对象 user:{"name":"旭"}
[形式参数]对象参数改名后,普通类型 name:阿导
[实际参数]普通参数改名后,普通类型 name:旭
[实际参数]普通参数改名后,对象 user:{"name":"旭"}
[形式参数]对象参数改名后,对象 user:{"name":"阿导"}
[实际参数]对象参数改名后,普通类型 name:旭
[实际参数]对象参数改名后,对象 user:{"name":"阿导"}

咋一看,普通类型改名没成功,对象改名成功了,那不是说明 java 即存在值传递,也存在引用传递?其实不然,下面我用两个正面例子来阐述对象传递为什么也是值传递。

正例

这边通过两次改名,并打印其传输的真正内容以及其内容指向的堆里面对象的内容,如下:


package com.dao.netty.test;

import com.alibaba.fastjson.JSON;

/**
 * 传递
 *
 * @author 阿导
 * @CopyRight 万物皆导
 * @Created 2019年02月20日 14:52:00
 */
public class PassDemo {

    public static void main(String[] args) {
        String name = "旭";

        User user = new User();
        user.setName(name);

        System.out.println("[地址值(实参)]改名之前,对象 user:"+ user);
        System.out.println("[地址指向的堆里面的对象(实参)]改名之前,对象 user:"+ JSON.toJSONString(user));

        changeName(user);
        System.out.println("[地址值(实参)]对象参数改名后,对象 user:"+ user);
        System.out.println("[地址指向的堆里面的对象(实参)]对象参数改名后,对象 user:"+ JSON.toJSONString(user));

        changeNameNew(user);
        System.out.println("[地址值(实参)]对象参数重新分配地址并改名后,对象 user:"+ user);
        System.out.println("[地址指向的堆里面的对象(实参)]对象参数重新分配地址并改名后,对象 user:"+ JSON.toJSONString(user));

    }

    private static void changeNameNew(User userNew){
        userNew = new User();
        userNew.setName("万物皆导");
        System.out.println("[地址值(形参)]对象参数改名后,对象 user:"+ userNew);
        System.out.println("[地址指向的堆里面的对象(形参)]对象参数改名后,对象 user:"+ JSON.toJSONString(userNew));
    }

    private static void changeName(User userChange){
        userChange.setName("阿导");
        System.out.println("[地址值(形参)]对象参数改名后,对象 user:"+ userChange);
        System.out.println("[地址指向的堆里面的对象(形参)]对象参数改名后,对象 user:"+ JSON.toJSONString(userChange));
    }
}


class User{
    /**
     * 姓名
     */
     private String name;


    public String getName() {
        return name;
    }

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

输出结果如下


[地址值(实参)]改名之前,对象 user:com.dao.netty.test.User@677327b6
[地址指向的堆里面的对象(实参)]改名之前,对象 user:{"name":"旭"}
[地址值(形参)]对象参数改名后,对象 user:com.dao.netty.test.User@677327b6
[地址指向的堆里面的对象(形参)]对象参数改名后,对象 user:{"name":"阿导"}
[地址值(实参)]对象参数改名后,对象 user:com.dao.netty.test.User@677327b6
[地址指向的堆里面的对象(实参)]对象参数改名后,对象 user:{"name":"阿导"}
[地址值(形参)]对象参数改名后,对象 user:com.dao.netty.test.User@5fa7e7ff
[地址指向的堆里面的对象(形参)]对象参数改名后,对象 user:{"name":"万物皆导"}
[地址值(实参)]对象参数重新分配地址并改名后,对象 user:com.dao.netty.test.User@677327b6
[地址指向的堆里面的对象(实参)]对象参数重新分配地址并改名后,对象 user:{"name":"阿导"}

显然,java 传输对象的时候,传的是引用地址,我们修改的是引用地址指向堆内存里面对象的值,就让我用图来接下调用 changeName(User userChange) 和 changeNameNew(User userNew) 两个方法的过程,如下:

  • changeName(User userChange)

changeName(User userChange)

  • changeNameNew(User userNew)
    changeNameNew(User userNew)

因此,可以证明,当我们重新分配空间,其实际参数并不会将指针指向新开辟的空间,内容也不会跟随形参变化而变化,因此 java 是值传递,若是引用传递,则实际参数内容中,name 也应该为 “万物皆导”,对象地址也应该为 com.dao.netty.test.User@5fa7e7ff 而不是之前的 user:com.dao.netty.test.User@677327b6。

需要云服务器的不要错过优惠

阿里云低价购买云服务,值得一看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值