讲解:按值传参与按引用传参
-------------------------------------------------------------------------------
类中包含方法,方法又分为方法声明和方法实现,方法声明中又有参数列表,参数根据调用后的效果不同,即是否改变参数的原始数值,又可以分为两种:按值传递的参数与按引用传递的参数。
他们的区别是什么呢?上面旺旺老师是说调用后的效果,我们看一个案例说明:比如有个女孩非常喜欢QQ,还给自己起了个浪漫的名字“轻舞飞扬”,飞扬小姐认识了一个网友“痞子蔡”(n年前非常火的一个网络小说《第一次亲密接触》的两个主人公),他们聊的很投缘,有天飞扬小姐竟然把自己的电话号码告诉了痞子蔡,有天痞子蔡竟然电话过来约飞扬小姐见面,考虑到网络的虚幻与现实人心的叵测,飞扬小姐面临着艰难的选择:是否去见网友?
那么见网友就是个方法,方法的参数就是轻舞飞扬,如果痞子蔡是个披着羊皮的狼,那飞扬小姐就可能面临危险,比如身上少些东西或者多些东西,就是说在方法体中有可能改变参数的原始数值。
现实中飞扬小姐只有两种选择,第一,为了爱情奋不顾身,上刀山下火海,再所不辞,但这有可能改变飞扬状态,即数值;第二,委婉拒绝以求自保,但如果痞子蔡为人特别好,比如像旺旺老师这样(呕吐中),她也许会失去一段大好的姻缘。这里,如果科技足够发达,我们可以完全给出第三种选择,轻舞飞扬制作一个自己的替身,即把自己备份一份,然后把备份传入方法体,这样不论痞子蔡对她做了什么都不会对她的源体发生影响,又能检测痞子蔡对自己是否真心。
OK,这里我们就把飞扬小姐本人去见网友叫按引用传递,这样在方法体中发生的改变在方法调用完对参数还有影响,而把让她替身去叫按值传递,这样方法调用完对参数原始数值没有影响,发生改变的只是参数的备份,这份备份在方法调用完会自动消亡,也就是说飞扬的替身在见完网友自动消亡。最后可以简单概括为一句话:按值传递参数数值不变,按引用传递参数数值改变。
我们上面刚学习了JAVA的数据类型,则有:值类型就是按值传递的,而引用类型是按引用传递的。下面看一个例子:
public class TestValueAndRef { public static void main(String[] args) { Student student = new Student(); student.stuAge = 10; int a = 10; int arr[] = new int[]{9, 5, 27};
System.out.println("初始值 a = " + a); System.out.println("初始值 student.stuAge = " + student.stuAge); System.out.println("初始值 arr[0] = " + arr[0]);
TestValueAndRef testValueAndRef = new TestValueAndRef(); testValueAndRef.change(a, student, arr);
System.out.println("调用函数后 a = " + a); System.out.println("调用函数后 student.stuAge = " + student.stuAge); System.out.println("调用函数后 arr[0] = " + arr[0]); } public void change(int pa, Student pstu, int[] parr) { //方法体中改变值类型pa的值 pa = pa + 10; //方法体中改变引用类型stu,parr的值 pstu.stuAge = pstu.stuAge + 10; parr[0] = parr[0] + 10;
System.out.println("方法体改变后pa = " + pa); System.out.println("方法体改变后pa = " + pa); System.out.println("方法体改变后parr[0] = " + parr[0]); } } |
运行结果:
初始值 a = 10 初始值 student.stuAge = 10 初始值 arr[0] = 9
方法体改变后pa = 20 方法体改变后pa = 20 方法体改变后parr[0] = 19
调用函数后 a = 10 调用函数后 student.stuAge = 20 调用函数后 arr[0] = 19 |
我们看到,基本数据类型int变量a虽然在方法体中改变了数值,但方法调用完后其原始数值并没有改变。而引用数据类型Student在方法体中改变年龄的数值,方法执行完其数值发生了改变,数组也是引用类型,所以其值也发生了改变。也就是说:按值传递参数数值不变,按引用传递参数数值改变。它们在内存中的变化如下所示:
根据上面讲解的值类型和引用类型的知识,int类型变量在栈中分配一块内存,而student与arr分配两块内存,当方法调用时,创建三个变量pa,pstu,parr这里相当于把栈中的数据全备份一份给这三个数值,则有:
大家看到,不管是按值传递还是按引用传递,都是把栈中的数据备份了一份给参数变量,只不过值类型备份的是具体的数值,而引用类型备份的是内存地址。
方法体执行完时:
我们看到,根据pstu与parr改变了堆中的具体数值,而pa改变的只是栈中的数值。最后方法调用结束,pstu,pa,parr三个变量消亡,则有:
根据这样的内存变换,您知道按值传递与按引用传递的深层原因了吗?
讲解:特殊的String类
-------------------------------------------------------------------------------
接下来我们看一个特例String类型,虽然它也是引用类型,但数值却不会改变。例:
public class TestString { public static void main(String[] args) {
String name = "wangwang";
TestString testString = new TestString(); System.out.println("方法调用前:" + name); testString.change(name); System.out.println("方法调用后:" +name); } public void change(String str) { str = "旺旺老师"; System.out.println("方法体内修改值后:" + str); }
} |
运行结果:
方法调用前:wangwang 方法体内修改值后:旺旺老师 方法调用后:wangwang
|
上例中,虽然参数String是引用数据类型,但其值没有发生改变,这是因为String类是final的,它是定长,我们看初始情况,即String name = "wangwang";这行代码运行完,如下图:
当调用方法时testString.change(name),内存变化为:
在方法体内,参数str赋予一个新值,str = "旺旺老师"。因为String是定长,系统就会在堆中分配一块新的内存空间37DF,这样str指向了新的内存空间37DF,而name还是指向36DF, 37DF的改变对它已没影响。
最后,方法调用结束,str与37DF的内存空间消亡。Name的值依然为wangwang,并没有改变。所以String虽然是引用类型参数,但值依然不变。
其他:旺旺老师视频与电子书下载地址:http://www.verycd.com/groups/@g481346/790989.topic