传递基本类型

看看这段代码

public static void main(String[] args) {
	int a = 10;
	foo(a);
	System.out.println(a);
}
	
public static void foo(int x){
	x = 20;
}

运行代码,输出结果:10

可以看到将 a 当做参数传递给 foo() 后,a 的值并未改变。

因为变量 a 是基本类型,声明 a 相当于向内存申请了一块空间,这一块空间保存的值是 10。将 a 传递给 foo(),只是将 a 的字面量传递,相当于 x 复制了一份 a 的值,对 x 做任何操作,都不会影响到 a ,所以这是值传递

传递引用类型

先来看第一个例子

public static void main(String[] args) {
	Dog dog = new Dog("zhangSan");
	System.out.println(dog.getName());
	foo(dog);
	System.out.println(dog.getName());
}
	
public static void foo(Dog d){
	d.setName("Lisi");
}

输出结果:

zhangSan
Lisi

可以看到 “zhangSan” 这条狗的名字改变了,变成了 “Lisi”,是不是就认为这是引用传递了呢?

当然不是的!

问题出现在这

Dog dog;

当你编写该定义时,你定义的是 Dog 类型的引用(这个引用保存在栈中),并不是 Dog 对象本身,如果你此时向 dog 发送一个消息,就会返回一个空指针异常。这是因为 dog 实际上并没有与任何事物相关联。

当你希望 dog 与一个新的对象相关联时,通常用 new 操作符来实现。

Dog dog = new Dog("zhangSan");

new 操作符在堆中创建了名为 “zhangSan” 的 Dog 对象,并将地址值(假如是 0x00)保存在名为 dog 的引用中,这样 dog 就指向了在堆中的对象,这时候使用 dog 就能向对象发送消息了。

再看下面这个例子

public static void main(String[] args){
    Dog dog = new Dog("zhangSan");
    Dog oldDog = dog;
    foo(dog);
    System.out.println(dog.getName());
    System.out.println(dog == oldDog);
}

public static void foo(Dog d){
    d = new Dog("Lisi");
}

输出结果:

zhangSan
true

将 dog 传递给 foo() 后,在 foo() 里 把它指向另一个新的对象,如果是引用传递,那么 dog 这个引用就应该重新指向这个新对象,但是从输出结果来看这是错误的。

原因是传递给 foo() 的,并不是 dog 这个引用本身,而是它所指向的在堆中对象的地址值。假如这个地址值是 0x00,那么可以这样理解,传递给 foo() 的,是 0x00。

我们在 foo() 里接受到这个地址值 0x00 后,可以给 0x00 所指向的对象发送消息,操作这个对象。形参 d 将对象的名字设置为 “Lisi” 后,dog 取出对象的名字也是 “Lisi”,这正如第一个例子所示。也就是说 d 这个形参和 dog 这个实参指向的是同一个对象,都保存着同一个地址值(0x00)。

但是将形参 d 重新指向另一个新对象,并不会对实参 dog 有任何影响,dog 还是仍然指向 0x00 这个地址值。

所以结论是,将 dog 传递给 foo(),并不是将引用本身传递,而是将引用所指向的地址值传递,也就是值传递

结论

无论是传递基本类型还是引用类型,都是值传递

参考资料

https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value?r=SearchResults

http://www.javadude.com/articles/passbyvalue.htm

原文链接:https://miansen.wang/2019/06/19/1/