很多程序设计语言(特别是, 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("Alice", . . . );
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 仍然引用这个方法调用之前所引用的对象(如图 4 - 8 所示 )
这个过程说明:Java 程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。
Go语言和java一样没有引用传递,即没有和C++一样以int&为类型的引用变量,虽然Go和C++一样有指针。
Go 值传递参数(按值传递)演示,类似于Java的值传递,因为改变的是形参副本指向的值地址,所以实参不变,是按值传递
package main
import "fmt"
func demo(i int, s string) {
i = 5
s = "改变"
}
func main() {
i := 1
s := "原值"
demo(i, s)
fmt.Println(i, s) //输出:1 原值
}
引用传递代码(按值传递)演示,类似于Java的引用传递,传递的数组变量相当于是数组的地址,改变数组内一项的址相当于改变数组属性的值,并没有改变数组变量指向的地址,所以是按值传递
package main
import "fmt"
func demo(arg []int) {
arg[len(arg)-1] = 110
}
func main() {
s := []int{1, 2, 3}
demo(s)
fmt.Println(s) //输出:[1 2 110]
}
改用指针 (按值传递)演示,类似于Java的引用传递,传递的指针变量相当于值的地址,函数改变的是地址存的值,而没有改变指针变量指向的地址,所以是按值传递。
package main
import "fmt"
//行参指针类型
func demo(i *int, s string) {
//需要在变量前面带有*表示指针变量
*i = 5
s = "改变"
}
func main() {
i := 1
s := "原值"
//注意此处第一个参数是i的地址,前面要加&
//s保留为值类型
demo(&i, s)
fmt.Println(i, s) //输出:5 原值
}
如果试着在函数里给i 指针变量赋予另一个值的地址,会发现实参并没有变,所以用指针传递也是按值传递。
package main
import "fmt"
//行参指针类型
func demo(i *int, s string) {
//需要在变量前面带有*表示指针变量
//给i赋予j的地址会发现没有变化
j := 5
i = &j
s = "改变"
}
func main() {
i := 1
s := "原值"
//注意此处第一个参数是i的地址,前面要加&
//s保留为值类型
demo(&i, s)
fmt.Println(i, s) //输出:1 原值
}