操作符之赋值

赋值
赋值使用操作符“=”。它的意思是“取右边的值(即右值),把它复制给左边(即左值)”。右值可以是任何常数,变量或者是表达式(只要它能生产一个值就行)。但左值必须是一个明确的,已命名的变量。也就是说,必须有一个物理空间可以存储等号右边的值。举例来说,可将一个常数赋值给一个变量:

			a = 4;

      但是不能把任何东西赋给一个常数,常数不能作为左值(比如不能说4=a)。
对基本数据类型的赋值是很简单的。基本类型存储了实际的数值,而并非指向一个对象的引用,所以在为其赋值的时候,是直接将一个地方的内容复制到另一个地方。例如,对基本数据类型使用a=b,那么b的内容就复制给a。若接着又修改了a,而b根本不会受到这种修改的影响。作为程序员,这正是大多数情况下我们所期望的。
但是在为对象“赋值”的时候,情况却发生了变化。对于一个对象进行操作时,我们真正操作的是对对象的引用。所以倘若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方,这意味这假若对对象使用c=d,那么c和d都指向原本只有d指向的那个对象:

			//:operators/Assignment.java
			//Assignment with objects is a bit tricky
			improt static net.mindview.util.Print.*;
			
			Class Tank {
					int level;
					}
			public class Assignment {
					public static void main(String[] args) {
						Tank t1 = new Tank();
						Tank t2 = new Tank();
						t1.level = 9;
						t2.level = 47;
						print("1:t1.level:"+t1.level+
								",t2.level:"+t2.level);
						t1=t2;
						print("2:t1.level:"+t1.level+
								",t2.level:"+t2.level);
						t1.level = 27;
						print("3:t1.level:"+t1.level+
								",t2.level:"+t2.level);
						}
				} /*Output:
				1:t1.level:9,t2.level:47;
				2:t2.level:47, t2.level:47;
				3:t1.level:27, t2.level:27;
				*///:~

      Tank类非常简单,它的两个实例(t1和t2)是在main()创建的。对每个Tank类对象的level域都赋予了一个不同的值,然后,将t2赋给t1,接着又修改了t1。在许多编程语言中,我们可能会期望t1和t2总是相互独立的,但由于赋值操作的是一个对象的引用,所以修改t1的同时也改变了t2!这是由于t1和t2包含的是相同的引用,他们指向相同的对象。(原本t1包含的对对象的引用,是指向一个值为9的对象。在对t1赋值的时候,这个引用被覆盖,也就是丢失了;而那个不在被引用的对象会由“垃圾回收器”自动清理)。
这种特殊的现象通常称作“别名现象”,是Java操作对象的一直基本方式,在这个例子中,如果想避免别名问题应该怎么办呢?可以这样写:

			t1.level = t2.level;

      这样便可以保持两个对象彼此独立,而不是将t1和t2绑定到相同的对象。但你很快就会意识到,直接操作对象内的域容易导致混乱,并且违背了良好的面向对象程序设计的原则,这不是一个小问题,所以从现在开始大家就应该留意,为对象赋值可能会产出意想不到的结果。
方法调用中的别名问题
将一个对象传递给方法时,也会产生别名问题:

			//:operators/PassObject.java
			//Passing objects to methods may not be
			//what you're used to.
			import static net.mindview.util.Print.*;
			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("1:x.c:"+x.c);
				f(x);
				priint("2:x.c:"+x.c);
				}
			}/*Output:
			1:x.c:a
			2:x.c:z
			*///:~

      在许多编程语言中,方法f()似乎要在它的作用域内复制器参数Letter y的一个副本;但实际上只是传递了一个引用。所以代码行

			y.c = 'z';

      实际改变的是f()之外的对象。
别名引起的问题及其解决办法是很复杂的话题,但是你现在就应该知道它的存在,并在使用中注意这个陷阱。
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章