概念
pass-by-value(按值传递):方法调用时,传递参数会在内存中开辟新的空间来存储参数,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相同的变量,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。离开函数体,不影响参数本身
pass-by-reference(引用传递):引用传递,则方法接收的是调用者提供的实际参数的地址,每次传递变量时,直接传递栈地址,而不做复制,方法体内和外指向同一个内存空间,里面改外面也会变。
背景知识
对基本数据类型,值就直接保存在变量中;对引用类型,变量中保存的只是实际对象的地址,一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容
JAVA中的值传递与引用传递
JAVA中只存在值传递!
JAVA程序设计语言总是采用值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容
* 对基本类型(byte,short,int,long,float,double,char,boolean,char)的函数参数传递是按值传递,一个方法是不可能修改一个基本数据类型的参数
public class Test {
private static int x=10;
public static void updateValue(int value){
value = 3 * value;
}
public static void main(String[] args) {
System.out.println("调用前x的值:"+x);
updateValue(x);
System.out.println("调用后x的值:"+x);
}
}
运行结果:
调用前x的值:10
调用后x的值:10
执行过程:
分析:
value被初始化为x值的一个拷贝(也就是10);value被乘以3后等于30,但注意此时x的值仍为10;这个方法结束后,参数变量value不再使用,被回收
* 对于非基本类型(数组,类,接口)的函数参数传递传递的是对象引用的拷贝,即传递的是引用变量内存储的对象内容的地址值,所以还是按值传递
对于引用类型的变量而言,它们的“内容”存放在托管堆里,而在线程栈里存放的是“内容”的“地址”。如对象在托管堆中的地址是x0001,那么栈地址0001的内存空间中存放的就是这个对象的内存地址x0001
注:当传递方法参数类型为引用数据类型时,一个方法不修改引用变量本身,但可以修改一个引用数据类型的参数所指向对象的值
public class Test {
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());
}
}
class User {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Name: " + name + " Age: " + age;
}
}
运行结果:
调用前user的值:Name: zhangsan Age: 26
调用后user的值:Name: Lishen Age: 18
执行过程:
分析:
student变量被初始化为引用user值的拷贝;调用student变量的set方法作用在这个引用的对象上,user和student同时引用的User对象内部值被修改;方法结束后,student变量不再使用,被释放,而user还是没有变,依然指向User对象
public class Test {
private static User user=null;
private static User stu=null;
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的值:Name: user Age: 26
调用前stu的值:Name: stu Age: 18
调用后user的值:Name: user Age: 26
调用后stu的值:Name: stu Age: 18
执行过程:
分析:
swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝的值而已,最终在方法结束后x,y将被丢弃,而原来的变量user和stu仍然引用这个方法调用之前所引用的对象。这个过程也充分说明了JAVA对对象采用的不是引用调用,而是对象引用进行的是值传递,一个方法是不可能修改参数本身的。
* 不可变类String\Integer\Double等
不可变类因为没有提供自身修改的函数,创建后,一旦更改等于是重新开了一块内存地址新生成一个对象
public static void change(String string2){
string2="zhangsan";
}
public static void main(String[] args) {
String string1=new String("abcd");
System.out.println(string1);
change(string1);
System.out.println(string1);
}
运行结果:
abcd
abcd
分析:
调用change函数后,两个字符串指向同一个Heap上的地址,string2修改内容后指向了新地址,但是string1仍然指向原地址