先说结论,Java就是值传递。
01
在Java中,所有的参数都是按值传递的,即方法得到的是所有参数值的一个副本,而不是原始参数。当我们将一个对象的引用作为参数传递给一个方法时,实际上传递的是该对象的引用的副本。
下面是一个例子来说明这一点:
public class Test {
public static void main(String[] args) {
int x = 10;
changeValue(x);
System.out.println("x = " + x); // 输出结果:x = 10
String str = "hello";
changeValue(str);
System.out.println("str = " + str); // 输出结果:str = hello
Person person = new Person("Jack");
changeValue(person);
System.out.println("person.name = " + person.getName()); // 输出结果:person.name = Rose
}
public static void changeValue(int x) {
x = 5;
}
public static void changeValue(String str) {
str = "world";
}
public static void changeValue(Person person) {
person.setName("Rose");
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在上面的代码中,我们分别定义了三个方法changeValue
,并且在main
方法中调用了这三个方法。
-
第一个
changeValue
方法接收一个int
类型的参数,将其修改为5
,但是在main
方法中输出的结果依然是原来的值10
,说明传递的参数是按值传递的。 -
第二个
changeValue
方法接收一个String
类型的参数,将其修改为world
,但是在main
方法中输出的结果依然是原来的值hello
,说明传递的参数是按值传递的。 -
第三个
changeValue
方法接收一个Person
类型的参数,将其名称修改为Rose
,并且在main
方法中输出该对象的名称也为Rose
,说明传递的参数是按值传递的。但是由于传递的是该对象的引用的副本,而不是原始参数,因此我们可以通过该引用访问和修改该对象的属性。
02
那为什么很多人都觉得Java是引用传递,比如对引用类型参数的传递?
相信这也是很多人的疑问。
产生这样的错觉,这很可能是因为Java中对象本身是通过引用来传递的。在Java中,当我们声明一个对象并将其赋值给一个变量时,实际上是创建了一个指向该对象的引用。当我们将该变量传递给一个方法时,实际上传递的是该引用,即指向该对象的地址。因此,在方法内部可以通过该引用来修改该对象的状态。
然而,需要注意的是,虽然引用本身是传递的,但是对于对象本身的修改并不会影响到原始的对象,因为传递的只是地址而不是对象本身。如果我们在方法内部将传入的参数重新赋值为一个新的对象,那么这个新对象只会存在于方法内部,并不会影响到原始的对象。
因此,我们可以说Java中是值传递,但是对于引用类型的参数,传递的是引用的值。
好的,让我们来看一个例子。
假设我们有一个Person类,其中包含姓名和年龄两个属性。现在有一个方法叫做changeAge,在该方法中,我们接收一个Person对象和一个整数作为参数,然后将这个整数赋值给Person对象的年龄属性。代码如下:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Tom", 20);
int newAge = 30;
changeAge(p1, newAge);
System.out.println(p1.getAge()); // 输出 20
}
public static void changeAge(Person person, int newAge) {
person = new Person(person.getName(), newAge);
}
}
在上面的代码中,我们创建了一个Person对象p1,其年龄为20。然后我们调用changeAge方法,并将p1和一个新的年龄值30传递给该方法。在changeAge方法中,我们通过传入的person参数来获取p1对象,并将其重新赋值为一个新的Person对象,其中年龄为传入的newAge参数。
最后,我们打印出p1对象的年龄,预期输出应该是30。但实际输出却是20。这是因为在changeAge方法中,我们只是修改了传入的person引用指向的对象,而并没有修改p1本身的值。当changeAge方法返回时,p1仍然指向原来的Person对象,其年龄还是20。
因此,我们可以看到,在Java中,虽然引用类型的参数传递的是引用的值,但是这不等同于引用传递。对于传入的引用类型参数,在方法内部对其进行修改只会影响到该引用所指向的对象,而不会影响到原始的对象。
03
看到这里,我知道有的小伙伴肯定坐不住了。
谁说不能改age,我这样写就能改!
public static void changeAge(Person person, int newAge) {
person.setAge(newAge);
}
我也不多废话了,直接告诉你结论:
person仍然是值传递,传递的是引用的值(内存地址),所以看起来像引用传递。
就好像你去配了一把钥匙,也能打开门一样,但是哪怕作用一样,你手里的也是配出来的钥匙,而不是原来的钥匙。
总结
对于基本数据类型,肯定是值传递,没什么好说的。
对于引用类型,仍然是值传递,传递的是引用的值(内存地址),所以看起来像引用传递。