学习来源:
出自:@Ryan Miao
链接:https://www.cnblogs.com/woshimrf/p/5263018.html
先看一段代码:
/**
* 调式运行,理解java的值传递
*/
public class ReferenceAndValue {
public String change(String s2, int i2, StringBuffer sb2, Person p2){
s2 = "123";
i2 = 3;
sb2.append("woshi");
p2.setAge(100);
sb2 = new StringBuffer("sbsb");
p2 = new Person("bb",44);
return s2;
}
@Test
public void testChange(){
StringBuffer sb = new StringBuffer("buff");
String s = "aaa";
int i = 1;
Person p = new Person("aa",12);
i=2;
change(s,i,sb,p);
System.out.println(s); //aaa
System.out.println(i); //2
System.out.println(sb.toString()); //buffwoshi
System.out.println(p); //aa,100
}
}
理解:
1、首先是String类型
抽取出String类型的主要代码 |
---|
![]() |
打断点,调式的过程如下
1.s指向aaa的后。
2、进入change方法后,赋值前。
可以看到进入change方法后,是形参s2来接收“aaa”的地址值,可以理解成把s的引用地址复制给s2。拓展理解:这算是由testChange“栈帧”来到了另一个change“栈帧”,关于栈帧介绍可以参考《深入理解Java虚拟机 》第3版。所以可以看到调式Variable中的变量都变成了s2,i2,sb2,p2。
3、在change方法中,完成s2的赋值后。
可以看到s2的引用地址值改变了,指向了“123”。由于String是不可变的
,所以新生成了一个字符串常量,然后s2指向了它。注意,原本已有的“aaa”字符串还是存在字符串常量池中。
4、执行完change方法,回到testChange方法。
小结:
可以看到testChange方法中的变量s作为实参,传递给change方法的形参s2,传递的是s的引用地址。这可以看成是把s的值(引用地址)复制了一份给s2,此时s2也就指向了"aaa";这就很好地理解了Java传参是值传递。
注意区分s的引用
和s的值
。
反过来想下,如果是引用传递,那传参应该传递的是s的引用
而不是s的值
,s的值
是引用对象地址@867。
2、然后是基本数据类型
基本数据类型传参就是值传参,这个就容易理解了。这里就不多加解释了,传参就是简单的值传递,由于i=2;所以把i值2传递给形参i2,那此时i2=2;然后在change方法里面对i2的值进行修改,i2=3,这个修改对testChange中的i值是没有影响的,所以testChange中的i值还是2。
3、再来看看StringBuffer
1、初始化StringBuffer sb,sb指向了@852;
2、进入change方法,执行append方法前
由形参sb2接收sb的引用地址值,此时sb2指向“buff”,注意sb2指向某个引用地址值
跟sb2本身的地址值是不一样的
。
3、sb2执行append方法后,sb2=new StringBuffer(“sbsb”)前;
可以看出,执行sb2.append(“woshi”),只是在sb2原来指向的引用地址值@852的字符串常量后添加"woshi"。
4、sb2=new StringBuffer(“sbsb”)后,返回testChange方法前。
注意,由于第三步执行了sb2.append(“woshi”);那之前的@852的值就变成了“buffwoshi”。然后,sb2指向了new StringBuffer(“sbsb”)生成的新地址@892。此时,testChange()方法中的sb依然指向@852,只不过@852的值变成了“buffwoshi”;见证在下一步,也就是返回testChange()方法后。
5、返回testChange方法后。
小结:
StringBuffer这小节的整个过程中,sb始终指向@852。然后在传参给sb2的时候,复制了一份引用对象的地址值@852给sb2,接着sb2对@852的值添加了“woshi”,再接着sb2指向了新的地址@892。最后返回testChange方法,sb的指向没有变。
4、Person引用对象
自定义类型在方法传参的过程也一样的道理。
大总结:
1、解答了String类型传参的时候,为什么在另一个方法里对String引用类型进行修改,然而在testChange方法中打印String变量还是原来的值,这是因为String类型是不可变的,它在change方法里赋新值,是另外创建了新的字符串常量,而不是对原来的字符串进行修改。
2、解答了为什么其他引用类型传参的时候,为什么它的值被改变了。这是因为传递的值就是Person引用对象的地址。然后在change方法中,对该引用对象地址的值进行了修改,所有在testChange方法中打印的值是改变的。
3、解答了为什么基本数据类型传参的时候,即使change方法在接收基本数据类型后,进行修改,而在testChange方法打印出来的值是没有变的。
注意:
- 对象本身的地址和引用类型对象的地址两者的区别
- String字符串常量是不可变的。如果对它进行赋值,则是改变了它的引用。
—end