3、正文: 1)、赋值操作: 例子源码:(Assign.java) [quote] public class Assign{ public static void main(String[] args){ int i = 1; Object o = new Object(); System.out.println("i = " + i + " ; o = " + o ); // Step 1 (示意图:3-1-1) int j = i; Object p = o; System.out.println("i = " + i + " ; j = " + j + " ; o = " + o + " ; p = " + p); //Step 2 (示意图:3-1-2) j++; p = new Object(); System.out.println("i = " + i + " ; j = " + j + " ; o = " + o + " ; p = " + p); //Step 3 (示意图:3-1-3) } } [/quote]
对上面例子的说明: (1),Step 1中,整数i和对象o得到赋值。从示意图3-1-1中可以看出:整数i存储在堆栈区(Stack);对象o的引用存储在了堆栈区,但是对象o的值却存储在了内存堆中(Heap),对象o的引用存储了对象o的地址。 Step 1在我的机器上的一次输出结果: [quote] i = 1 ; o = java.lang.Object@a90653 [/quote] 至于对象o的值输出来怎么会是那个样子,我只能告诉您:在java程序的一次运行过程中,每个Object对象输出这样的值是唯一的,因此可以借此来判断对象的引用指向的对象是否发生了改变。详情请参考Java API 文档(下同,这里给出的是:J2SE 1.5.0 API 中文版): http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/io/PrintStream.html#println(java.lang.Object) http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/lang/Object.html#toString() (2),Step 2中,把整数i赋值给了整数j,把对象o赋值给了对象p。从示意图3-1-2中可以看出:整数i的值复制给了整数j,整数j同样存储在堆栈区;存储在堆栈区的对象o的引用中存储的对象o的地址C复制给了对象p的引用,对象p的引用同样在堆栈区中。因为对象p的引用得到了对象o的引用复制过来的对象o的值的存储地址C,所以对象p的引用和对象o的引用都指向了在堆(heap)中的同一个对象,并且,这个对象的地址是地址C。 Step 2在我的机器上的一次输出结果: [quote] i = 1 ; j = 1 ; o = java.lang.Object@a90653 ; p = java.lang.Object@a90653 [/quote] (3),Step 3中,整数j的值加1,赋给了对象p新的对象值。从示意图3-1-3中可以看出:整数i的值不变,整数j的值加1变为2,整数在堆栈区中;新生成的对象的值存储在了堆(Heap)中,地址为F。新生成对象的地址F存储在了堆栈区p的引用中,替换了原来存储在其中的地址C。于是,p的引用就指向了新生成的对象,这个新生成的对象的地址是地址F。而整数i和对象o的??(包括对象o的引用)没有改变也不曾有任何改变(除了初次赋值)。 Step 3在我的机器上的一次输出结果: [quote] i = 1 ; j = 2 ; o = java.lang.Object@a90653 ; p = java.lang.Object@de6ced [/quote]
至此,通过上面的例子及其示意图和说明,我得到一个结论: 在Java赋值操作中,针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的对象的值的地址进行复制。这就是术语:“堆栈区数据复制(Stack Data Copy,简称SDC)”在Java赋值操作中的阐述。
2)、方法中的参数传递操作: 例子源码:(PassParameter.java) [quote] public class PassParameter{ static void showMe(int pi, Object po){ System.out.println("pi = " + pi + " ; po = " + po); // Step 2 (示意图:3-2-2) pi++; po = new Object(); System.out.println("pi = " + pi + " ; po = " + po); // Step 3 (示意图:3-2-3) } public static void main(String[] args){ int i = 1; Object o = new Object(); System.out.println("i = " + i + " ; o = " + o); // Step 1 (示意图:3-1-1) showMe(i, o); System.out.println("i = " + i + " ; o = " + o); // Step 4 (示意图:3-2-3) } } [/quote]
对上面例子的说明: (1),Step 1中,与上面Assign.java中的Step 1相同,略。 Step 1在我的机器上的一次输出结果: [quote] i = 1 ; o = java.lang.Object@a90653 [/quote] (2),Step 2中,与上面Assign.java中的Step 2类似,只是Assign.java中的整数j和对象p变成了这里的方法showMe()中的参数:整数pi和对象po。并且,由于这里是参数传递,把Assign.java示意图3-1-2中的“=”替换成PassParameter.java示意图3-2-2中的“<--”,以此表示是参数传递。据我的理解,它们是一回事。 Step 2在我的机器上的一次输出结果: [quote] pi = 1 ; po = java.lang.Object@a90653 [/quote] (3),Step 3和Step 4合并起来,见示意图3-2-3同样,与上面Assign.java中的Step 3类似。 Step 3和Step 4在我的机器上的一次输出结果: [quote] pi = 2 ; po = java.lang.Object@de6ced i = 1 ; o = java.lang.Object@a90653 [/quote]
至此,通过上面的例子及其示意图和说明,我得到一个结论: 在Java方法参数传递操作中,针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的对象的地址的值进行复制。这就是术语:“堆栈区数据复制(Stack Data Copy,简称SDC)”在Java方法参数传递操作中的阐述。
4,结论 综上所述:在Java中,不管是赋值操作还是方法的参数传递操作--针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的对象的值的地址进行复制。 所以,据我的理解,术语:“堆栈区数据复制(Stack Data Copy,简称SDC)”能够有助于理解在Java中进行赋值和传递参数的机制,能够有助于在一定程度上消除“传值”、“传引用”等语义上的多变性的负面影响,可以提出来供大家交流。