一、概念
实际上对这两种传递方式,知乎上有个回答说得很好:
值传递和引用传递,属于函数调用时参数的求值策略(Evaluation Strategy),这是对调用函数时,求值和传值的方式的描述,而非传递的内容的类型(内容指:是值类型还是引用类型,是值还是指针)。
值类型/引用类型,是用于区分两种内存分配方式,值类型在调用栈上分配,引用类型在堆上分配。
一个描述内存分配方式,一个描述参数求值策略,两者之间无任何依赖或约束关系。
实际上意思就是,在函数调用时,不要通过传进去的参数类型去判断是值传递还是引用传递,而是要看这种语言具体的求值策略。
二、详解
在函数调用过程中,调用方提供实参,这些实参可以是常量:
Call(1); // 常量 Call(x); // 变量 Call(2 * x + 1); // 常量与变量的组合 Call(GetNumber()); // 对其它函数的调用
但是所有这些实参的形式,都统称为表达式(Expression)。
求值(Evaluation)即是指对这些表达式的简化并求解其值的过程。
求值策略(值传递和引用传递)的关注的点在于,这些表达式在调用函数的过程中,求值的时机、值的形式的选取等问题。求值的时机,可以是在函数调用前,也可以是在函数调用后,由被调用者自己求值。这里所谓调用后求值,可以理解为Lazy Load或On Demand的一种求值方式。
除了值传递和引用传递,还有一些其它的求值策略,详见下表:
求值策略 |
求值时间 |
传值方式 |
值传递(Pass by value) |
调用前 |
值的结果(是原值的副本) |
引用传递(Pass by reference) |
调用前 |
原值(原始对象,无副本) |
名传递(Pass by name) |
调用后(用到才求值) |
与值无关的一个名 |
重点是值传递和引用传递。上面给出的传值方式的表述有些单薄,下表列出了一些二者在行为表象上的区别:
值传递 |
引用传递 |
|
根本区别 |
会创建副本(Copy) |
不创建副本 |
结果 |
函数中无法改变原始对象 |
函数中可以改变原始对象 |
这里的改变不是指mutate, 而是change,指把一个变量指向另一个对象,而不是指仅仅改变属性或是成员什么的,所以说Java是Pass by value,原因是它调用时Copy,实参不能指向另一个对象,而不是因为被传递的东西本质上是个Value。
这些行为,与参数类型是值类型还是引用类型无关。对于值传递,无论是值类型还是引用类型,都会在调用栈上创建一个副本,不同是,对于值类型而言,这个副本就是整个原始值的复制。而对于引用类型而言,由于引用类型的实例在堆中,在栈上只有它的一个引用(一般情况下是指针),其副本也只是这个引用的复制,而不是整个原始对象的复制。
综上所述,对于Java的函数调用方式最准确的描述是:参数藉由值传递方式,传递的值是个引用。(句中两个“值”不是一个意思,第一个值是evaluation result,第二个值是value content)。
三、例子
1. 值类型与引用类型
int num = 10; String str = "hello";
num是基本类型,值就直接保存在变量中。而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容,比较特别的是,string对象是不可变对象
2. 函数调用
1) 基本类型
void foo(int value) {
value = 100;
}
foo(num); // num 没有被改变
传进foo()里的只是num的一个副本。
2) 没有提供改变自身方法的引用类型
void foo(String text) { text = "world"; } str=”hello” foo(str); // str 也没有被改变
整个过程如下图所示:
3) 提供了改变自身方法的引用类型
StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder.append("4"); } foo(sb); // sb指向内容被改变了,变成了"iphone4",但本身还是指向同样的地址。
append前:
append后:
4)
提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符
StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder = new StringBuilder("ipad"); } foo(sb); // sb指向内容没有被改变,还是 "iphone"。
赋值前:
赋值后:
四、参考
(完)