提纲: 1、版权声明 2、前言 3、正文 4、结论 5、附注 6、参考资料
=======================================================
1、版权声明: 本文作者:little,原始出处:,邮件:mdx-xx@tom.com。如有任何反馈意见请联系作者,作者会在本文原始出处随时更新此文。转载及引用请保留此版权声明,谢谢。
2、前言: 关于Java的赋值和参数传递是按值(by value)进行的还是按引用(by reference)进行的,这个问题曾经迷惑了很多人,包括我。而且,我想,这个问题还将继续迷惑一些人,包括那些C++的高手。 在这里,我不准备用“按值(by value)”和“按引用(by value)”这样的术语来阐述这个问题。因为,从字面的理解来看,这样的术语在不同的人头脑里有不同的含义。我试图从Java数据(包括原始类型(primitive type)和对象(ojbect))在内存中的存储这个角度,用一个自创的“术语”来阐述我对这个问题的理解。这个术语就是:“堆栈区数据复制(Stack Data Copy,简称SDC)”。详细一点就是:在Java中,不管是赋值操作还是参数传递操作--针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的对象的值的地址进行复制。 像上面抠字眼的句子读起来比较费力,我在后面将用两个例子并结合一些示意图来阐述我对这个问题的理解。希望各位朋友帮助纠正错误。
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中进行赋值和传递参数的机制,能够有助于在一定程度上消除“传值”、“传引用”等语义上的多变性的负面影响,可以提出来供大家交流。
5,附注: 由于本人水平有限,上面的一切全是基于实践进行的带有一些推测成分在内的个人心得总结。我也以上面的自创术语去成功解释过一些文章中的有关问题(如下面参考资料中的例程)。谨希望在能为部分Java初学者提供一个理解Java赋值和参数传递的手段的同时,更能得到各位朋友的斧正,以便能够对这个问题形成更加正确和准确的认识。在我提高认识的同时,我会在本文原始出处随时更新此文。再次贴出我的邮件:mdx-xx@tom.com。谢谢。
6,参考资料:
1),Java参数传递方式 http://www.jiehoo.com/java-pass-parameter.htm 2),破除java神话之二:参数是传址的http://www.javaresearch.org/article/showarticle.jsp?column=544&thread=443 3),Java 应用程序中的按值传递语义http://www.javaresearch.org/article/showarticle.jsp?column=1&thread=706 4),我对《Java 应用程序中的按值传递语义》的理解http://www.javaresearch.org/article/showarticle.jsp?column=1&thread=3156 5),Thinking in Java, 3rd Edition in Java[download]http://www.mindviewinc.com/downloads/TIJ-3rd-edition4.0.zip[/download]
全文完 2006年11月22日午
|
<iframe marginwidth="0" marginheight="0" src="/ad_tech.htm" frameborder="0" width="160" scrolling="no" height="600"></iframe>
|
相关推荐
用 Java 实现数据结构中的堆栈 用 Java 实现数据结构中的堆栈
java堆栈信息dump文件
常用数据结构(堆栈,队列,列表)JAVA代码
这是教学的ppt 可用于大学本专课件 也可用于自学
Java script实现堆栈和链表,实现了堆栈和链表的所有功能。
堆栈实现的计算器-数据结构课程设计,应用字符优先级
java dump 堆栈 dumpAnalyzer 分析,在日常工作中,经常会遇到,系统跑着跑着就会出现性能问题,CPU居高不下。这个时候我们就需要对系统的堆栈信息进行分析。这里就介绍如何使用IBM内存检测工具(dumpAnalyzer)。
java堆栈详解
Java代码实现的堆栈,有入栈,出栈操作,以及堆栈应用中的括号匹配问题,以及编译系统中将后缀表达式的运算
打印JAVA堆栈的方法
文章介绍了使用Java编程语言实现了基于数组的Java堆栈,并实现了一些基本的堆栈方法
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 4、文字...
Java堆栈分析操作详细说明,找到内从泄露原因
Java 把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的 一些基本类型的变量和对象的引用变量都是在函数 的栈内存中分配。当在一段代码块中定义一个变量时,java 就在栈中 为这个变量分配内存空间...
java 堆栈的演示程序 jsp程序 源代码设计 下载 欢迎您的下载
java的堆栈java的堆栈java的堆栈java的堆栈java的堆栈java的堆栈java的堆栈
jss7, RestComm Java SS7堆栈和服务 RestComm Java SS7堆栈和服务 简介开放源码 Java SS7栈,允许Java应用与遗留SS7通信设备通信。jSS7为专用设备( 对话框) 提供了一个开源软件解决方案,实现了 M3UA 。SCCP 。TCAP
数据结构讲义之堆栈
java 泛型 容器 堆栈
全书内容浅显易懂,利用大量且丰富的图示与范例, 详解复杂的抽象理论,从最基本的数据结构概念开始 说明,再以Java工具加以诠释阵列结构、堆栈、链表 、队列、排序、查找等重要的概念,引领读者抓住重 点轻松进入...