Java求值策略:值传递与引用传递
内存中变量的存储方式
- 基本类型 基本类型指int,double,short 等类型,新建变量直接在内存中开辟对应大小的内存,数据直接存储在变量所表示的内存中。
- 引用类型 创建对象的时候,在内存中开辟一段区域存储对象,将该区域的地址赋值给变量。所以变量存储的是一个地址。
两种存储方式如下图

'='的作用
- 基本类型 修改变量对应内存的值。
- 引用类型 若=后的对象不存在,则开辟一段新的区域存储新的对象,并将变量存储的地址指向新的地址;若已存在,则直接将变量存储的地址指向新的地址。

求值策略
目前主要有三种方式
| 求值策略 | 求值时间 | 传值方式 | | ---------------------------| ----------------- | ---------------------- | | 值传递(pass by value) | 调用前 | 值的结果(是原值的副本) | | 引用传递(pass by reference) | 调用前 | 原值(原始对象,无副本) | | 名传递(pass by name) | 调用后(用到才求值) | 与值无关的一个名 |
求值策略 | 求值时间 | 传值方式 |
值传递(pass by value) | 调用前 | 值的结果(是原值的副本) |
引用传递(pass by reference) | 调用前 | 原值(原始对象,无副本) |
名传递(pass by name) | 调用后(用到才求值) | 与值无关的一个名 |
所以,值传递,传递的是原值的一个副本,无法改变(mutate)原始对象,而引用传递,传递的是原来的值,可以改变对象。二者的区别是,是否会创建副本(注意,这里的改变指的是改变变量在内存中的值)
考虑如下函数
public void changeObj(Employee e){ e=new Employee(); e.salary=10000; } public void changeValue(int a){ a=10; } public static void main(String[] args) { Employee emp=new Employee(); emp.salary=8000; changeObj(emp) int value=100; changeValue(value); }
首先声明: Java是值传递
- 基本类型 上述代码中的changeValue函数,值传递,调用时会在内存中创建局部变量a,并且赋值100(作为value的副本),所以函数修改了a的值,但是value的值没有发生变化。
- 引用类型 上述的changeObj函数,值传递,传递的是emp的值,emp的值实际上是一个地址,所以函数调用时,会创建一个局部变量e作为emp的副本,其值为emp的值,也是一个地址,指向内存中的emp对象存储的位置。所以代码只是将e的值指向了一个新的地址,不会修改emp的salary。
如果注释掉
e=new Employee();
呢?此时,e指向的对象和emp指向的对象是一个对象。所以当修改e.salary的时候,内存中对象的salary值被改变,所以emp.salary也被修改了。
总结
综上所述,对于Java的函数调用方式最准确的描述是:参数藉由值传递方式,传递的值是个引用。(句中两个“值”不是一个意思,第一个值是evaluation result,第二个值是value content)由于这个描述太绕,而且在字面上与Java总是传引用的事实冲突。于是对于Java,Python、Ruby、JavaScript等语言使用的这种求值策略,起了一个更贴切名字,叫Call by sharing。这个名字诞生于40年前。