1. 概念
1.1 值传递和引用传递
在程序设计语言中参数传递给方法(或函数)的方式分为两种:① 按值调用(call by value),② 按引用调用(call by reference),所谓的按值调用表示方法接收的是:被调用参数提供的数值,而按引用调用则表示方法接收的是被调用参数提供的变量地址。变量地址在C语言中被称为指针,在Java中被称为引用。
Java程序设计语言采用按值调用(call by value)的方式进行参数传递,也就是说方法得到的是所有参数值的一个拷贝,方法并不能修改传递给它的任何参数变量的内容。为了更好的理解这句话,我们先了解Java中的数据类型
1.2 Java中的数据类型
在Java程序设计语言中数据类型可分为:基本数据类型和引用数据类型。其中基本数据类型的变量中存放的是该变量的值;而引用数据类型的变量中存放是该变量的引用,例如:
public class TestPet {
public static void main(String[] args) {
int a = 10;
int arr = {1,2,3};
Pet p = new Dog();
}
}
class Dog Pet{
String name;
String type;
public Dog() {
}
public void move() {
System.out.println("run");
}
}
对应的内存分析图如下:
整形数据变量a中存放的是值10;数组arr和对象p中存放的分别是引用1bd23456、23cf34452
2. Java中的数据传递
2.1. 传递参数为基本类型数据
直接上代码:
package com.ppw.test0726;
/**
* java中的按值调用
* @author panpan
*/
public class CallByValue {
public static void main(String[] args) {
int x=10;
System.out.println("调用前x的值:"+x);
updateValue(x);
System.out.println("调用后x的值:"+x);
}
public static void updateValue(int value){
value = 3 * value;
}
}
程序运行结果为:
调用前x的值:10
调用后x的值:10
我们来分析下代码执行的过程:当程序进入主函数main()以后,首先为x赋值10后执行语句System.out.println("调用前x的值:"+x),“输出调用前x的值:10”; 然后调用updatValue()函数,并将实参x的值拷贝一份传递给形参value,此时x和value值均为10,然后程序进入该函数的内部,执行value = 3 * value,value变为30,x不变仍为10,随后程序调用完updateValue()函数回到主函数,执行System.out.println("调用后x的值:"+x),输出“调用后x的值:10”。
分析程序执行过程发现:实参传递给形参的过程,就是将实参的值拷贝一份传递给形参,方法内部修改的是形参value,而未对实参x做任何修改,根据value的作用域可知,当调用完updateValue()后,value的生命周期结束,被GC回收了。
这里给大家提供一个程序代码,供大家思考:程序的输出结果是?
package com.ppw.test;
/**
* java中的按值调用
* @author panpan
*/
public class TestValue {
public static void main(String[] args) {
int x=10;
System.out.println("调用前x的值:"+x);
updateValue(x);
System.out.println("调用后x的值:"+x);
}
public static void updateValue(int x){
x = 3 * x; // 此处的x是updateValue中的x,与main方法中的x无关
}
}
总结一下:当向方法中传递的参数类型为基本数据类型(数字以及布尔值)时,这个方法是不能修改实参中的值的,本质是因为将实参中存储的值拷贝一份传递给形参,
2. 2. 传递参数为引用类型数据
直接上代码:创建一个User类
public class User {
private String name;
private int age;
public User(){
}
public User(String name, int age) {
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
创建一个测试类:
/**
* java中的按值调用
* @author panpan
*/
public class TestAddress {
private static User user=null;
public static void updateUser(User student){
student.setName("Lishen");
student.setAge(18);
}
public static void main(String[] args) {
user = new User("zhangsan",26);
System.out.println("调用前user的值:"+user.toString());
updateUser(user);
System.out.println("调用后user的值:"+user.toString());
}
}
运行结果为:
调用前user的值:User [name=zhangsan, age=26]
调用后user的值:User [name=Lishen, age=18]
我们分析程序代码的执行过程:当程序执行到user = new User("zhangsan", 26),会在堆内存中开辟空间用来存放对象user中的属性,并将该空间的首地址传递给user,假设地址为@1a1c1234,然后调用System.out.println("调用前user的值:"+user.toString()); 输出结果“调用前user的值:User [name=zhangsan, age=26]”,在执行updateUser()时,将实参user中存储的地址传递给形参student中,即student指向user指向的地址,当在updateUser()函数中执行student.setName(Lishen)和student.setAge(18)时,student根据user提供的地址找到set方法更改了name和age属性,跳出updateUser函数后执行System.out.println("调用后user的值:"+user.toString()); 输出“调用后user的值:User [name=Lishen, age=18]”。
通过对程序执行过程分析,我们发现:实参传递给形参的过程,就是将实参user中存放的的地址值拷贝一份传递给形参student,方法内部修改的是形参student指向的地址(user)中存放的内容(name,age),而未对实参user做任何修改,也不能堆实参user做任何修改。student之所以能修改user中属性,实际上是因为student根据user提供的地址找到了user中name和age,因此可以修改
总结:当向方法中传递的参数类型为引用数据类型时,这个方法可以修改一个引用数据类型的参数所指向对象的值。
值的注意的是:Java程序语言中采用值传递,即无论传递参数的类型是引用数据类型还是基本数据类型,都是将实参中存放的值拷贝至形参中,方法不能改变实参中存放的值,对于基本数据类型这个值是基本数据类型的数值;对于引用数据类型这个值是地址。
我们在看下面这段代码,理解上面这段话,
/**
* java中的按值调用
* @author panpan
*/
public class CallByValue {
private static User user=null;
private static User stu=null;
/**
* 交换两个对象
* @param x
* @param y
*/
public static void swap(User x,User y){
User temp =x;
x=y;
y=temp;
}
public static void main(String[] args) {
user = new User("user",26);
stu = new User("stu",18);
System.out.println("调用前user的值:"+user.toString());
System.out.println("调用前stu的值:"+stu.toString());
swap(user,stu);
System.out.println("调用后user的值:"+user.toString());
System.out.println("调用后stu的值:"+stu.toString());
}
}
运行结果为:
调用前user的值:User [name=user, age=26]
调用前stu的值:User [name=stu, age=18]
调用后user的值:User [name=user, age=26]
调用后stu的值:User [name=stu, age=18]
运行结果告诉我们user和stu的值并没有发生变化,也就是说方法并没有改变存储在变量user和stu中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝的值而已。在方法结束后x,y将被丢弃,而原来的变量user和stu仍然引用这个方法调用之前所引用的对象。
这个过程也充分说明了java程序设计语言对对象采用的不是引用调用,实际上是对象引用进行的是值传递,不论是传递基本数据类型还是引用数据类型,java函数只是拷贝了实参存储的值,基本数据类型的实参存储的数值,引用数据类型的实参存储的是地址,之所以形参能修改引用数据是因为形参和实参同时指向了一个对象
边学习,边总结,此篇文章参考链接如下,如有侵权,联系删除