结论:在Java中,参数传递都是值传递,没有引用传递
形参和实参
参数一般可以分为形式参数和实际参数,先看一下各自的定义。
形参:在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
实参:在主函数中调用另一个函数时,函数中传递的就是实际参数(括号中的参数)。
示例:
public class Test {
public static void main(String[] args) {
Test pt = new Test();
//“张三”为实际参数
pt.sout("张三");
}
public void sout(String name) {
//name为形式参数
System.out.print(name);
}
}
值传递和引用传递
值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
(1)基本类型参数测试:
public class Test {
public static void main(String[] args) {
Test test = new Test();
int i = 100;
System.out.println("inc方法调用前,i的值为=" + i);
test.inc(i);
System.out.println("inc方法调用后,i的值为=" + i);
}
private void inc(int i) {
i ++;
System.out.println("inc方法中,i的值为=" + i);
}
}
结果:
inc方法调用前,i的值为=100
inc方法中,i的值为=101
inc方法调用后,i的值为=100
可以看出,main方法里的变量i,并不是inc方法里的i,inc方法内部对i的值的修改并没有改变实际参数i的值,改变的只是inc方法中i的值,因为inc方法中的i只是main方法中变量i的复制品。
因此容易得出结论:Java中,一个方法不可能修改一个基本数据类型的参数 ,所以是值传递。
(2)引用类型参数测试:
public class Test {
public static void main(String[] args) {
Test test = new Test();
User user = new User();
user.setUsername("张三");
user.setAge(20);
System.out.println("updateUser方法调用前,user为" + user.toString());
test.updateUser(user);
System.out.println("updateUser方法调用后,user为" + user.toString());
}
private void updateUser(User updateUser) {
updateUser.setUsername("李四");
System.out.println("updateUser方法中,user为" + updateUser.toString());
}
static class User {
private String username;
private Integer age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
}
执行结果:
updateUser方法调用前,user为User{username='张三', age=20}
updateUser方法中,user为User{username='李四', age=20}
updateUser方法调用后,user为User{username='李四', age=20}
Java中传递引用数据类型的时候也是值传递,为什么呢?
因为复制的是参数的引用(地址值),并不是引用指向的存在于堆内存中的实际对象,简图如下图所示:
main方法中的user是一个引用(也就是一个指针),它保存了User对象的地址值,当把user的值赋给updateUser()方法的user形参后,即让updateUser()方法的user形参也保存了这个地址值,即也会引用到堆内存中的User对象。
上面代码中,之所以产生引用传递的错觉,是因为参数保存的是实际对象的地址值,你改变的只是地址值指向的堆内存中的实际对象,并没有真正改变参数,参数的地址值没有变。
场景引入:
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?
什么是真正的改变参数?
public class Test {
public static void main(String[] args) {
Test test = new Test();
User user = new User();
user.setUsername("张三");
user.setAge(20);
System.out.println("updateUser方法调用前,user为" + user.toString());
test.updateUser(user);
System.out.println("updateUser方法调用后,user为" + user.toString());
}
private void updateUser(User user) {
//改变了user参数,指向了一个新的地址
user = new User();
user.setUsername("李四");
user.setAge(50);
System.out.println("updateUser方法中,user为" + user.toString());
}
static class User {
private String username;
private Integer age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
}