本文参考java核心卷1
首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按 值调用(call by value)表示方法接收的是调用者提供的值。而按引用调用(call by reference) 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而 不能修改传递值调用所对应的变量值。“按……调用”(call by)是一个标准的计算机科学术语, 它用来描述各种程序设计语言(不只是Java)中方法参数的传递方式(事实上,以前还有按 名调用(call by name), Algol程序设计语言是最古老的高级程序设计语言之一,它使用的就 是这种参数传递方式。不过,对于今天,这种传递方式已经成为历史)。
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷 贝,特别是,方法不能修改传递给它的任何参数变量的内容。
例如,考虑下面的调用:
double percent = 10;
harry.raiseSalary(percent);
不必理睬这个方法的具体实现,在方法调用之后,percent的值还是10。
下面再仔细地研究一下这种情况。假定一个方法试图将一个参数值增加至3倍:
public static void tripleValue (double x) // doesn't work
(
x = 3 * x;
)
然后调用这个方法:
double percent = 10;
tripleValue (percent);
不过,并没有做到这一点。调用这个方法之 后,percent的值还是10。下面看一下具体的执行 过程:
- ) x被初始化为percent值的一个拷贝(也就 是 10)。
- x被乘以3后等于30。但是percent仍然 是10 (如图所示)。
这个方法结束之后,参数变量x不再使用。
然而,方法参数共有两种类型:
・基本数据类型(数字、布尔值)。
・对象引用。
读者已经看到,一个方法不可能修改一个基 本数据类型的参数。而对象引用作为参数就不同了,可以很容易地利用下面这个方法实现将 一个雇员的薪金提高两倍的操作:
public static void tripleSalary (Employee x) // works
{
x.raiseSalary(200);
}
当调用
harry = new Employee(...);
tri pleSalary(harry);
时,具体的执行过程为:
- ) X被初始化为harry值的拷贝,这里是一个对象的引用。
- ) raiseSalary方法应用于这个对象引用。x和harry同时引用的那个Employee对象的薪 金提高了 200%
- )方法结束后,参数变量x不再使用。当然,对象变量harry继续引用那个薪金增至3 倍的雇员对象
读者已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法 得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。
很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引 用调用。有些程序员(甚至本书的作者)认为Java程序设计语言对对象采用的是引用调用, 实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详 细地阐述一下这个问题。
首先,编写一个交换两个雇员对象的方法:
public static void swap(Employee x, Employee y) // doesn't work
Employee temp = x;
x = y;
y = temp;
}
如果Java对对象采用的是按引用调用,那么这个方法就应该能够实现交换数据的效果:
Employee a = new Employee ("Al ice",...);
Employee b = new Employee("Bob",...);
swap(a, b);
// does a now refer to Bob, b to Alice?
但是,方法并没有改变存储在变量a和b中的对象引用。swap方法的参数x和y被初始 化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。
// x refers to Alice, y to Bob
Employee temp = x;
x = y;
y = temp;
// now x refers to Bob, y to Alice
最终,白费力气。在方法结束时参数变量X和y被丢弃了。原来的变量a和b仍然引用 这个方法调用之前所引用的对象
这个过程说明:Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按 值传递的。
下面总结一下Java中方法参数的使用情况:
・一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
・一个方法可以改变一个对象参数的状态。
・一个方法不能让对象参数引用一个新的对象。
程序清单4-4中的程序给出了相应的演示。在这个程序中,首先试图将一个值参数的值 提高两倍,但没有成功:
TestingtripleValue:
Before: percent=10.0
End of method: x=30.0
After: percent=10.0
随后,成功地将一个雇员的薪金提高了两倍:
TestingtripleSalary:
Before: salary=50000.0
End of method: salary=150000.0
After: salary=150000.0
方法结束之后,harry引用的对象状态发生了改变。这是因为这个方法可以通过对象引用 的拷贝修改所引用的对象状态。
最后,程序演示了 swap方法的失败效果:
Testing swap:
Before: a=Alice
Before: b=Bob
End of method: x=Bob
End of method: y=Alice
After: a=Alice
After: b=Bob
可以看出,参数变量x和y交换了,但是变量a和b没有受到影响。
代码展示:
package com.dhy.boot;
import java.time.LocalDate;
public class Employee {
private String name;
private Double salary;
private LocalDate hireDay;
public Employee(String name, Double salary, LocalDate hireDay) {
super();
this.name = name;
this.salary = salary;
this.hireDay = hireDay;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void setHireDay(LocalDate hireDay) {
this.hireDay = hireDay;
}
@Override
public String toString() {
return "Employee [name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
}
public void raiseSalary(Double byPercent) {
double raise = salary*byPercent/100;
salary+=raise;
}
public static void main(String[] args) {
double percent = 10;
tripleValue(percent);
System.out.println(percent);
Employee harry = new Employee("duhaiyang", 100d, LocalDate.now());
tripleSalary(harry);
System.out.println(harry);
Employee harry1 = new Employee("pengfei", 200d, LocalDate.now());
swap(harry,harry1);
System.out.println(harry);
System.out.println(harry1);
}
public static void tripleValue(Double x) {
x = 3*x;
}
public static void tripleSalary(Employee x) {
x.raiseSalary(200d);
}
public static void swap(Employee x,Employee y) {
Employee temp = x;
x = y;
y = temp;
}
}