深入浅出Java值传递

Java只有值传递

其实无论传递的参数是基本类型还是引用类型,都会在栈中创建一个副本。不同的是对于基本类型直接拷贝的是原始值(因为该变量在栈中存储);对于引用类型来说,栈中存储的是对象的引用(对象的地址),而真实的对象在堆中(对象在堆中被创建),此时你拷贝一份引用(地址),那么它还是指向堆中对象(地址不变嘛)

首先了解一下实参和形参

实参:在调用时传递给函数的参数
形参:定义函数名和函数体时使用的参数

例如

public class ValueTest {
    public static void main(String[] args) {
        int a = 10;
        test(a);	//实际参数a
    }
    public static void test(int s) {	//形式参数s
        s = 20;
    }
}

简单了说就是:实参–>传入;形参–>接收

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

然后看下面的代码,当传递的是基本类型时

public class ValueTest {
    public static void main(String[] args) {
        int a = 10;
        System.out.printf("调用方法前a的值为%d",a);
        test(a);
        System.out.println();
        System.out.printf("调用方法后a的值为%d",a);
    }
    public static void test(int s) {
        s = 20;
    }
}

在这里插入图片描述

为什么会出现这个结果呢?很多人肯定都知道,变量的拷贝嘛。看下面的图形分析

执行流程:

1.main方法入栈,栈中初始化了一个变量a=10
2.调用了test方法,栈中拷贝了一个副本变量a=10
3.修改副本变量对原始变量a没有任何影响
在这里插入图片描述
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

当传递的是引用时

public class ValueTest {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername("zsh");
        user.setPassword("zsh");
        System.out.printf("修改之前User的数据为%s\n",user);
        test(user);
        System.out.printf("修改之后User的数据为%s",user);
    }
    public static void test(User temp) {
        temp.setUsername("ad");
        temp.setPassword("ad");
    }
}

在这里插入图片描述

这是因为什么呢?且看下面的分析
在这里插入图片描述

当传递的是引用类型时,也会在栈中对其进行拷贝,只不过此时拷贝的是地址,由于副本和原始的地址相同,所以两者都指向堆中的User对象;一旦对副本的引用进行操作(0x98.username=ad,0x98.password=ad),则相当于修改了堆中对象的数据

看下面的debug模式下的分析
在这里插入图片描述
在这里插入图片描述

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

当我传递的是String类型时,会出现什么情况呢?

当我浏览网上关于传递的是String类型时,发现很多人讲的是真的离谱,完全没有看到重点,明明一个final可以解决的,非要扯一堆东东。

public class ValueTest {
    public static void main(String[] args) {
       String str = "zsh";
       System.out.printf("调用方法前str为:%s\n",str);
       test(str);
        System.out.printf("调用方法后str为:%s",str);
    }
    public static void test(String temp) {
        temp = "Ronin";
    }
}

在这里插入图片描述
是不是感到匪夷所思,我们都知道String也是对象,那为什么调用前后的数据相同呢?

其实这要追溯到String的底层源码

private final byte[] value;

从这个final关键字你就明白了,final修饰了value,所以value只能指向该字节数组地址空间(数组值可以改变);所以当你修改时,其实是相当于重新new了一个对象。

在这里插入图片描述

流程分析

1.首先调用函数,传递拷贝的地址副本,我们都知道和上面传递的User引用一样
2.然后当执行到temp = "Ronin"时,由于String类型的不可变性,所以会重新创建一个String对象,将temp指向新的String对象(将地址赋给temp)。

最后结果如下
在这里插入图片描述
debug下分析地址变化

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面测试一下你理解值传递没

public class ValueTest {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername("zsh");
        user.setPassword("zsh");
        System.out.printf("before:%s\n",user);
        test(user);
        System.out.printf("after:%s\n",user);
    }
    public static void test(User temp) {
        temp = new User();
        temp.setUsername("af");
        temp.setPassword("af");
    }
}

结果:

before:User{username=‘zsh’, password=‘zsh’}
after:User{username=‘zsh’, password=‘zsh’}

这个就不用多说了吧,temp = new User();创建一个User对象后,将地址赋给temp,然后main中的引用和test中的引用就完全是两码事了

总结

总的来说无论传递的是基本类型还是引用类型,本质上都是值传递;只不过基本类型是对变量值的拷贝,引用类型是对地址值的拷贝(地址值也是值)。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thecoastlines

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值