既然说到参数传递,那么就得说说 Java 中方法参数的类型。Java 中方法参数的类型有两种:
- 基本数据类型(数字,布尔值)
- 对象引用
另外,参数传递的方法,也可以分为两种,一种是值传递,表示方法接收的是调用者提供的值;另一种就是引用传递,表示方法接收的是调用者提供的变量地址。
Java 中的参数传递采用的是值传递的方式,也就是说方法得到的是所有参数值的一个拷贝。
对于基本数据类型而言,值传递的方式并不能改变参数的值。但是对于引用类型的参数而言,值传递可以改变对象的状态,因为,这里的”值“指的是引用的拷贝(副本),多个引用指向同一个对象,任何一个引用对对象进行了修改都会改变该对象。
那么我们就来看一下下面的例子。
参数为基本数据类型:
public class PassByValue {
public static void main(String[] args) {
int value = 5;
System.out.println("Before: value=" + value);
tripleValue(value);
System.out.println("After: value=" + value);
}
public static void tripleValue(int x) {
System.out.println("InnerBefore: x=" + x);
x *= 3;
System.out.println("InnerAfter: x=" + x);
}
}
运行结果为:
Before: value=5
InnerBefore: x=5
InnerAfter: x=15
After: value=5
我们来分析一下方法执行的过程:
- 参数变量 x 初始化为参数值的一个拷贝,也就是 5
- 对 x 进行扩大 3 倍的操作,此时,x 的值变为 15
- 方法结束后,x 不再被使用
可以看到,至始至终,原始变量的的值都没有受到影响,影响的只是它的拷贝。所以,对于基本数据类型而言,不能改变其值。
参数为对象引用:
public class PassByValue {
public static void main(String[] args) {
Person hellen = new Person("Hellen", 15);
System.out.println("Before: age=" + hellen.getAge());
Person.increaseAge(hellen);
System.out.println("After: age=" + hellen.getAge());
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void increaseAge(Person p) {
System.out.println("InnerBefore: age=" + p.getAge());
p.raiseAge(3);
System.out.println("InnerAfter: age=" + p.getAge());
}
public void raiseAge(int increment) {
this.age += increment;
}
public int getAge() {
return this.age;
}
private String name;
private int age;
}
运行结果为:
Before: age=15
InnerBefore: age=15
InnerAfter: age=18
After: age=18
执行过程分析:
- 将参数变量 p 初始化为 hellen 值的拷贝,这里的值是对象的引用
- p 将引用对象的年龄加 3。此时,p 和 hellen 同时引用的那个 Person 对象的年龄被加 3
- 方法结束后,p 不再被使用,而 hellen 依旧引用那个年龄被加 3 的对象
到目前为止,相信大家也发现了,似乎这里是传递的参数是引用并不是值,那为什么说 Java 中采用的是值传递呢?首先,个人理解,Java 中参数传递的的确是值,只不过这里的值指的是引用的拷贝而已。其次,如果 Java 中采用的是引用传递,那么方法中交换两个对象就会导致两个对象引用变量引用的对象也相互交换。如果并没有发生交换的话,也就印证了 Java 中只参数传递采用的是值传递的方式。
参数为对象引用,交换两个对象:
public class PassByValue {
public static void main(String[] args) {
Person mike = new Person("Mike", 17);
Person jack = new Person("Jack", 18);
System.out.println("Before: mike=" + mike.getName() + ", jack=" + jack.getName());
Person.swap(mike, jack);
System.out.println("After: mike=" + mike.getName() + ", jack=" + jack.getName());
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void swap(Person p1, Person p2) {
System.out.println("InnerBefore: p1=" + p1.getName() + ", p2=" + p2.getName());
Person temp = p1;
p1 = p2;
p2 = temp;
System.out.println("InnerAfter: p1=" + p1.getName() + ", p2=" + p2.getName());
}
public String getName() {
return this.name;
}
private String name;
private int age;
}
运行结果为:
Before: mike=Mike, jack=Jack
InnerBefore: p1=Mike, p2=Jack
InnerAfter: p1=Jack, p2=Mike
After: mike=Mike, jack=Jack
方法执行过程分析:
- 参数变量 p1 与 p2 分别初始化为 mike 和 jack 值的拷贝
- 交换 p1 和 p2,此时,p1 和 p2 分别引用交换后的对象
- 方法结束后,p1 和 p2 都不再使用,mike 和 jack 依旧引用此方法调用前的对象
前面的一系列测试说明,Java 中的参数传递采用的是值传递的方式而不是引用传递。