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

1.结论

java是值传递

为啥先说结论呢,因为好多文章总是一大篇推论,人都看晕了,最后才说结论,导致印象不深

解释

java里面其实只有一种参数传递方式就是按值传递,为什么有争议,原因是参数类型分为引用类型基本类型

  1. 如果参数传递的是基本类型,接收的其实是原始值副本,操作副本并不会对原始值产生影响
  2. 如果参数传递的是引用类型,接收的其实是原始值的引用地址,而不是值的副本,因此如果函数修改了这个参数的值,实际上是修改内存地址的值,从而影响原始值

JVM为什么这样设计?

为了提升效率,节省资源

  • 基本类型和引用类型拷贝副本的效率不一样,基本类型更快

  • 引用类型一般占空间比较大,拷贝副本会比较浪费内存,所以设计为传递引用地址

对象访问定位

https://blog.csdn.net/xiewenfeng520/article/details/85319048
对象的引用地址是存储在栈的reference里面,指向java堆中的内存地址
在这里插入图片描述

2.全方位证明

2.1包装类或者基本数据类型

场景:定义Integer类型a和b,交换a,b对象

why?为啥要将Integer和int一起说
虽然Integer是引用类型,但是源码实现中,Integer使用final类型的int进行存储的,final类型的变量不能被重新赋值,所以操作参数传递变量时,实际上是操作的变量对象的副本
和int类型的实现原理是一样的,都是操作变量的副本

java中的包装类型都是默认使用这种方式实现的,使用拷贝副本的方式提升效率和减少内存消耗

在这里插入图片描述

代码

public class IntegerTest {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        System.out.println("before----a=" + a + ",b=" + b);
        swap(a, b);
        System.out.println("after----a=" + a + ",b=" + b);
    }

    private static void swap(Integer a, Integer b) {
        System.out.println("running----a=" + a + ",b=" + b);
        Integer tmp;
        tmp = a;
        System.out.println("running----tmp="+tmp);
        a = b;
        b = tmp;
        System.out.println("running----a=" + a + ",b=" + b+ ",tmp=" + tmp);
    }

执行结果

在这里插入图片描述

结论

从这个结果可以看出,swap方法传递的是值,因为交换a,b的值之后,原来的对象没有发生变化;
如果是引用传递,交换的是对象引用的地址,a,b就会发生变化,实际上却没有;
其实swap中传递的a,b只是传递的a,b值的副本,对副本进行操作不会影响原来的值。

备注

将Integer类型替换为基本类型int,或者是String等结果都是一样的(博主亲测)

2.2集合类型

场景:定义List集合a和b,交换a,b对象

代码

 public static void main(String[] args) {
        List a = new ArrayList();
        a.add(1);
        List b = new ArrayList();
        b.add(2);
        System.out.println("before----a=" + a + ",b=" + b);
        swap(a, b);
        System.out.println("after----a=" + a + ",b=" + b);
    }

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

执行结果

[外链图片转存失败(img-cGZlujYp-1564537866008)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1564537591142.png)]

结论

和2.1中的结论一致

2.3 引用类型(争议点)

场景:定义User类型a和b,交换a,b对象

代码

public class ObjectTest {
    public static void main(String[] args) {
        User a = new User("1");
        User b = new User("2");
        System.out.println("before----a=" + a + ",b=" + b);
        swap(a, b);
        System.out.println("after----a=" + a + ",b=" + b);
    }

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

user:

public 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() {
//        +super.toString()
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

执行结果

在这里插入图片描述

误区

public class ObjectTest {
    public static void main(String[] args) {
        User a = new User("1");
        System.out.println("before----a=" + a );
        swap(a);
        System.out.println("after----a=" + a );
    }

    private static void swap(User a) {
        a.setName("2");
//        a = new User("2");
    }
}

执行结果

在这里插入图片描述

推导结论

第一个例子和我们2.1中的结论一致,但是第二个例子看似对变量a做了修改。
如果将a.setName("2");换成a = new User("2");,a的值不会发生变化
在这里插入图片描述

  • 第一种setName的方式实际上是拿到对象栈的reference中的引用地址,找到了堆中的内存地址进行了修改,并不是直接对传递过来的原始对象进行修改
  1. 拿到对象的引用地址
  2. 对引用地址指向的内存地址的值进行修改

直接对对象进行修改,应该是开辟一块新的内存地址替换掉原始对象的引用地址

  • 第二种new的方式则是将一个新的内存地址赋值给了原始对象参数引用的副本,所以并没有对原始对象产生影响,如果是引用传递会直接将新的内存地址替换掉原始值的内存地址

然而a的值并没有发生变化,更加证实了参数传递的不是引用而是值

结论

引用类型的参数传递,是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数

3.最后结论

值传递引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参

综上所述java参数传递是值传递

4.彩蛋

解决Integer a,b值交换问题
分享一道java面试题 Integer值交换

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值