赋值、引用
此内容摘自《Thinking in java》Page39.
赋值使用操作符“=”。它的意思是“取右边的值(即右值),把它复制给左边(即左值)”。右值可以是任何常数、变量或者表达式(只要它能生成一个值就行)。但是左值必须是一个明确的、已命名的变量。也就是说,必须有一个物理空间可以存储等号右边的值。举例来说,可将一个常数赋给一个变量:
a = 4;
但是不能把任何东西赋给一个常数,常数不能作为左值(比如不能4=a;)。
对基本数据类型的赋值是很简单的。基本类型存储了实际的数值,而并非指向一个对象的引用,所以在为其赋值的时候,是直接将一个地方的内容复制到了另一个地方。例如,对基本数据类型使用a=b,那么b的内容就赋值给a。若接着又修改了a,而b根本不会受这种修改的影响。作为程序员,这正是大多数情况下我们希望看到的。
但是在为对象“赋值”的时候,情况却发生了变化。对一个对象进行操作时,我们真正操作的是对对象的引用。所以倘若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。这意味着假若对对象使用c=d,那么c和d都指向原本只有d指向的那个对象(如果c此前包含的对对象的引用是指向另一个对象,那么在执行c=d赋值的时候,这个引用被覆盖,也就是丢失了;而那个不再被引用的对象将被“垃圾回收器”自动清理(注意条件:不可达时才会被清理))。由于赋值操作的是一个对象的引用,所以此时修改c的时候也就相当于修改了d!这是由于c和d包含的是相同的引用,它们指向相同的对象。
这种特殊的现象通常被称作“别名现象”,是java操作对象的一种基本方式。
方法调用中的别名问题
将一个对象传递给方法时,也会产生别名问题:
class Letter{ char c;}public class PassObject{ static void f(Letter y){ y.c = 'z'; } public static void main(String[] args){ Letter x = new Letter(); x.c = 'a'; print(x.c); //打印a f(x); print(x.c); //打印z }}var obj = {}; // 空对象 var ref = obj; // 引用obj.name = "objectA" ; alert(ref.name); //ref跟着添加了name属性 打印objectAobj = ["one", "two", "three"]; //obj指向了另一个对象(数组对象)alert(ref.name); //ref还指向原来的对象 打印objectAalert(obj.length ); //打印3alert(ref.length); //打印undefinedobj只是对一个匿名对象的引用,所以,ref并非指向它,当obj指向另一个数组对象时,可以看到,引用ref并未改变,而始终指向这那个后来添加了name属性的"空"对象"{}"