错误理解①:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。
错误理解②:Java是引用传递。
错误理解③:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。
基本数据类型: byte char short int long float double boolean
引用数据类型: 接口类型(List) 、类类型(ArrayList) 、数组(int [] arr)、String
1、基本数据类型参数传递
基本数据类型中,传递的是值;形参是对实参的一份临时拷贝,修改形参的值不会对实参产生任何影响。
例子:
public class Test {
public static void main(String[] args) {
int a = 0;
modify(a);
System.out.println("实参:"+a);
}
private static void modify(int A) {
A = 999;
System.out.println("形参:"+A);
}
}
-------------------------------------------------
打印结果:
形参:999
实参:0
下面通过画图分析内存中的变化来理解为什么会这样
- 变量main 方法中的a是基本类型,所以它的值0直接存储在栈中。
- 调用 modify() 方法的时候,将为实参a创建一个副本(形参 A),它的值也为0,开辟了新的内存空间,在栈中的其他位置。
- 对形参A的任何修改都只会影响它自身而不会影响实参。
可见,modify方法内部对形参A的值的修改并没有改变实际参数a的值。那么,按照上面的定义,
有人得到结论:Java的方法传递是值传递。
这显然是错误的的,请往下看
2、引用数据类型参数传递
首先对于Demo demo = new Demo()这样的代码
请问demo 是引用还是对象?
通过分解我们来看
Demo demo;
demo = new Demo();
- 假如 demo是对象的话,就不需要通过 new 关键字创建对象了,那也就是说,demo并不是对象,在“=”操作符执行之前,它仅仅是一个变量。
- 真正的对象是new Demo( ),它是对象,存储于堆中;
- “=”操作符将对象的引用赋值给了 demo变量,于是 demo此时应该叫对象引用,它存储在栈中,保存了对象在堆中的地址。
每当引用类型作为参数传递时,都会创建一个对象引用(实参)的副本(形参),该形参保存的地址和实参一样。
class Demo {
public String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Demo{" +
"name='" + name + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) {
Demo demo = new Demo();
demo.setName("包包子");
function(demo);
System.out.println("主函数"+demo);
}
public static void function(Demo demoCopy) {
demoCopy.setName("九度");
System.out.println("function函数"+demoCopy);
}
}
-------------------------------------------------
打印结果:
function函数Demo {name='九度'}
主函数Demo {name='九度'}
通过画图来理解一下
- 在调用 function() 方法时,实参demo和在栈中创建了一个新的副本demoCopy, 它的值也是"包包子"在堆中的内存地址, 与demo指向的对象是一致的
- function()方法中,修改了形参demoCopy的name为"九度",意味着对象demoCopy的 name从"包包子"变成了"九度", 而两个引用指向的是同一块内存空间,所以原本demo对象的值也会跟着被修改
举个生活中的例子来理解一下,你有一把钥匙能打开你家的房门,你重新复制了一把新的钥匙给了你朋友,因为这两把钥匙都能打开你家的这扇门,所以假如当你朋友开了门去你家把电视剧砸了,这对你有影响吗?当然是的,因为开的是你家的门。
看到这里,有人得出一个新的结论:Java的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。
这显然是错误的结论,不信请往下继续看
3、"传引用"一定能成功修改值吗?
请看这个例子
class Demo {
public String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Demo{" +
"name='" + name + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) {
Demo demo = new Demo();
demo.setName("包包子");
function(demo);
System.out.println("主函数"+demo);
}
public static void function(Demo demoCopy) {
demoCopy = new Demo();
demoCopy.setName("小鱼干");
System.out.println("function函数"+demoCopy);
}
}
-------------------------------------------------
打印结果:
function函数Demo{name='小鱼干'}
主函数Demo{name='包包子'}
你一定很疑惑,我不是明明向function函数中传了引用吗,为什么没能成功修改主函数中对象的属性???
下面通过画图来分析一下为什么传引用不一定会成功修改值,最后再告诉你为啥Java中永远是值传递
-
当我们在main中创建一个Demo对象的时候,在堆中开辟一块内存,其中保存了name数据。然后demo持有该内存的地址0x7796(图①)。
-
当尝试调用function方法,并且demo作为实际参数传递给形式参数demoCopy的时候,会把这个地址0x7796交给user,这时,user也指向了这个地址(图②)。
-
在function方法内对参数进行修改的时候,即demoCopy = new Demo();,会重新开辟一块0X6688的内存,赋值给demoCopy。后面对demoCopy的任何修改都不会改变原内存0X7796的内容(图③)。
上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在执行demoCoopy= new Demo();的时候,实际参数的引用也应该改为指向0X6688,但是实际上并没有。
通过以上分析,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数
Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用,即一个地址。
再举个生活中的例子:
还是你有一个房子一把钥匙,你复制了一把钥匙给你的朋友;但是你朋友中途自己买了一间新房子附带一把新房子的新钥匙。持有的钥匙从你的更新为新钥匙,你朋友拿着新钥匙打开了新房子,砸了里面的电视机,请问这对你家的电视机有影响吗,显然没有,因为操作的都不是一个房子里的电视机
综上:正确结论 Java中只有值传递,没有引用传递!!!