java是值传递还是引用传递?
java是值传递还是引用传递?
java到底是什么传递?这个问题我在网上看到过很多答案,一下子把我也整得有点懵,所以我在知乎发了一篇帖子,想咨询一下大佬们的看法,结果也是说什么的都有。最后我结合值传递和引用传递的概念还有真实的案例得出了结论:java是值传递。
首先,我们先来明白两个概念:
值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
如果还不太理解的话,我们可以引入一张图:
大家应该对实参和形参的概念应该不陌生,在上图中,num是一个实参,method函数中的a是一个形参,值传递认为:a实际上是num复制出来的,这样在函数中对a的值是不会影响num的值。引用传递认为:在进行函数调用的时候,是把num的地址直接传给a,这样a和num在内存中保存的其实是一个值,修改了a的值之后,num也会跟着改变。
接下来我们从基本数据类型和引用数据类型来分析。
基础数据类型
话不多说,我们直接上代码:
public static void main(String[] args) {
int num = 100;
char str = 'a';
method(num, str);
System.out.println("num===>: " + num);
System.out.println("str===>: " + str);
}
public static void method(int a, char b) {
a = 1000;
b = 'b';
System.out.println("a===>: " + a);
System.out.println("b===>: " + b);
}
/*运行结果:
a===>: 1000
b===>: b
num===>: 100
str===>: a*/
运行结果我们可以看出,在method方法中改变了形参的值,但是它并没有影响实参的值,符合值传递的概念。
其实这个时候我们已经可以看到基础数据类型是值传递了,但是为了更清晰的了解,我们还是来画图说明。
因为是基础类型,所以我们并不考虑堆和方法区的存储,我们可以看到变量是存储在栈中的,其中method内部是新建了两个值,它对值的修改只会修改局部变量,并不会影响main方法中的值。
引用数据类型
首先我们先定义一个Person类
public class Person {
private String name;
private int age;
private String sex;
// get set 以及构造方法省略
}
定义一个personOne对象
public static void main(String[] args) {
Person personOne = new Person();
personOne.setName("某云");
personOne.setSex("男");
personOne.setAge(18);
System.out.println(personOne);
System.out.println(personOne.getName());
System.out.println(personOne.getSex());
System.out.println(personOne.getAge());
/*运行结果
包名.Person@4554617c
某云
男
18*/
}
运行结果中我们可以分析出来personOne的值实际上是 包名.Person@4554617c这是一个地址值。
我们先来看第一个例子:
public static void main(String[] args) {
Person personOne = new Person();
personOne.setName("某云");
personOne.setSex("男");
personOne.setAge(18);
System.out.println(personOne);
System.out.println(personOne.getName());
System.out.println(personOne.getSex());
System.out.println(personOne.getAge());
method(personOne);
System.out.println("===========");
System.out.println(personOne);
System.out.println(personOne.getName());
System.out.println(personOne.getSex());
System.out.println(personOne.getAge());
}
public static void method(Person methodPerson){
methodPerson.setName("某腾");
methodPerson.setSex("女");
methodPerson.setAge(19);
}
/*
包名.Person@4554617c
某云
男
18
===========
包名.Person@4554617c
某腾
女
19
*/
从运行结果来看,personOne被改变了,然后这种现象就会被大家误以为是引用传递,System.out.println(personOne);但是大家看这一句的打印结果是没有发生改变的,也就是说personOne的值并没有被改变,它始终指向包名.Person@4554617c,下面我们画图来说明一下。
new的所有对象都是存储在堆里面,我们从最开始就看到了直接打印变量,他返回的是包名.Person@4554617c说明变量里存储的实际是地址值,进入method方法中的时候会复制出来一个变量methodPerson,我们通过复制出来的变量可以直接访问堆里面new出来的对象,并且更改其中的属性,看起来很像引用传递,但是大家不要急,我们来看最后一个例子。
我们将第一个例子中的代码稍微更改一下:
public static void main(String[] args) {
Person personOne = new Person();
personOne.setName("某云");
personOne.setSex("男");
personOne.setAge(18);
System.out.println(personOne);
System.out.println(personOne.getName());
System.out.println(personOne.getSex());
System.out.println(personOne.getAge());
method(personOne);
System.out.println("===========");
System.out.println(personOne);
System.out.println(personOne.getName());
System.out.println(personOne.getSex());
System.out.println(personOne.getAge());
}
public static void method(Person methodPerson){
Person personTwo = new Person();
personTwo.setName("某腾");
personTwo.setSex("女");
personTwo.setAge(19);
methodPerson = personTwo;
System.out.println("methodPerson ===========");
System.out.println(methodPerson);
System.out.println(methodPerson.getName());
System.out.println(methodPerson.getSex());
System.out.println(methodPerson.getAge());
}
大家先来猜测一下结果,personOne的值会被改变吗?
答案是不会。
/*
包名.Person@4554617c
某云
男
18
methodPerson ===========
包名.Person@74a14482
某腾
女
19
===========
包名.Person@4554617c
某云
男
18
*/
不知道大家猜对了没有,methodPerson = personTwo;我明明给methodPerson赋值了,但是它为什么没有像例子一那样改变main函数中personOne的值呢。
我们继续看图:
personOne的值是0x333,进入到方法之后用methodPerson接收,methodPerson的初始值也是0x333。
现在定义一个personTwo,它指向的是0x666,最后我们将personTwo赋值给methodPerson;
程序走到这methodPerson改变了,但是personOne的值并没有改变。
总结
最后总结一下,为什么例子一可以改变,因为java形参中所接收的是实参的copy,在方法中我们可以通过这个copy找到堆内存中存储的数据从而改变堆内存,所以你并没有改变personOne的值,你只能改变personOne的值对应的堆内存的值,就像是最后一个例子看到的那样,methodPerson只是personOne的副本,你对这个副本的所有更改并不会影响personOne。
最后再回顾一下概念:
值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
如果是引用传递的话,我在最后一个例子中改变methodPerson的值,personOne是应该要随之变化的,但是事实是并没有变化。
再来看值传递,上述所有例子都可以说明,方法内部其实是将实参进行copy,不管是什么类型,所有的更改都是针对copy出来的副本的,所以证实:java所有的都是值传递。

浙公网安备 33010602011771号