论坛首页 Java企业应用论坛

Java 是传值还是传引用?关于String的疑惑

浏览 50207 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-05-13  
j0hnny 写道

不是我不厚道,我确实问的是传参的问题,没有考虑到String是final的,当时没有想那么多,只是想弄明白是怎么回事。并不是通过这个混淆大家的思路,这个我也是后来才知道的。


final class只是表示这个String Class 不能被继承。
String 是 Constant ,这种说法看来引起了混淆。
我建议,不要看到什么名词,就按照自己的想象去套。
(这不是针对楼主一个人的建议,而是一个普遍的建议 :-) )

String 不能被修改,事实上很简单。就是因为 String 没有提供写操作方法,没有提供 能够修改String 对象的成员变量 的方法。
比如,String的 toLowerCase, toUpperCase, substring, 等等 操作方法都是 生成新的String 对象,而不是修改本身内部的成员变量。return new String object.
StringBuffer.append() 方法,则直接修改本身内部的成员变量。有兴趣,可以参见StringBuffer的源代码。所有的append() 方法都 return this。

rootsoso 写道
kao 都是老问题了 前面很多的帖子都讨论过的,仔细看的话就会楼主就好明白啦

http://forum.iteye.com/viewtopic.php?t=11649

http://forum.iteye.com/viewtopic.php?t=12447


你给出的这两个帖子涉及到 String 的 JVM 规范,java 语法规范,以及编译优化等内容。和楼主的问题没有太大关系。而且,那两个帖子都远远没有把这个看似简单的 "String == equals" 问题讨论清楚(我也参与了类似的讨论好多次,但都是就事论事,而没有全面的剖析)。如果有时间,我会写一篇关于这个问题的详细剖析的帖子。

楼主的这个问题 主要是 参数传递的问题。
这里涉及到的基本功常识为:
1. 每个Thread有自己的 运行栈。Stack

2. 每次函数调用,运行栈顶部上都会为 该函数调用 分配一个Stack Frame.  这也称为Context.

3. 函数调用结束,栈指针回到上一级函数调用的Context, Stack Frame, 刚才分配的顶部Stack Frame 就失效。上一级函数成为当前Stack Frame, Context。

4. 每次函数调用, 参数都会 压栈 压入运行栈。注意,这是非常重要的。

5. 压入运行栈内的 参数,是 Copy 了一份。注意,这也是非常重要的。所以,对参数 赋值,对上一级函数调用来说,是根本没有意义的。
因为只是改变了顶部Stack Frame里面 参数Copy的内容,根本不对上一级 Stack Frame 造成任何影响。

这里,我 不厌其烦地、好为人师的 普及这些 基本功,主要是看到太多的资料 误人子弟。不知道是出于什么样的目的,总是在名词说法上绕圈子,而不从最基本的事实讲解入手。
编程基本功这个阵地,无产阶级不占领,资产阶级就要占领。:lol:
汇编基本功,编译原理基本功 一定要普及。
上述叙述的事实,如果用过汇编,那是非常容易理解的。
函数调用之前,一定要自己手工 PUSH EX. 把参数的一个copy 放到寄存器里面。
0 请登录后投票
   发表时间:2005-05-13  
buaawhl 写道

我 不厌其烦地、好为人师的 普及这些 基本功
 
老师好

buaawhl 写道

5. 压入运行栈内的 参数,是 Copy 了一份。

这个参数是什么?可以通过这个参数干什么?可能这是j0hnny 的问题核心
0 请登录后投票
   发表时间:2005-05-13  
怎么说呢,我猜j0hnny要么一开始没有表达清楚自己真正的疑问是什么,要么从头到尾都没有明确“值”、“引用”以及“mutable”、“immutable”的概念
这种概念性的问题本来就很难解释,如果j0hnny自己不出来仔细描述一下自己对这些概念的看法的话,大家很容易打错目标
如果从头说起,那真是繁杂得很哪,反而引出更多的概念来
什么是mutable,什么是immutable
什么是stack,什么是heap
什么是变量,什么是对象本体
变量保存什么,对象本体保存什么
变量本身保存在哪儿,对象本体保存在哪儿
什么是地址,什么是引用
什么是传值,什么是传地址,什么是传引用
传的是谁的值,谁的地址,谁的引用
……
不画上几张图恐怕是很难解释详细,说不得还要讲讲x86体系
那可头疼死了,所以俺一开始就避重就轻,简单对比C/C++的情况
一直比较佩服buaawhl的认真态度
0 请登录后投票
   发表时间:2005-05-13  
neooen 写道
buaawhl 写道

我 不厌其烦地、好为人师的 普及这些 基本功
 
老师好

buaawhl 写道

5. 压入运行栈内的 参数,是 Copy 了一份。

这个参数是什么?可以通过这个参数干什么?可能这是j0hnny 的问题核心


:D 别这么客气。我只是有时 “好”为人师。一种爱好。而不是真的有资格充当老师。:oops:  

参数就是 楼主 给出的 passA,  str (String),  str (String Buffer)。
被复制的就是这些东西,复制到运行栈里面。
这个参数就是给 被调用的函数 用的。这个函数可以读参数的值,调用参数的方法(如果是一个Object),当然也可以用 = 给参数赋值。只不过这个赋值 只是改变了 备份的内容。唉,这里又牵扯到 赋值 这个动作 以及对应指令的 具体内容了。左值。

厌倦发呆 写道

怎么说呢,我猜j0hnny要么一开始没有表达清楚自己真正的疑问是什么,要么从头到尾都没有明确“值”、“引用”以及“mutable”、“immutable”的概念
这种概念性的问题本来就很难解释,如果j0hnny自己不出来仔细描述一下自己对这些概念的看法的话,大家很容易打错目标
如果从头说起,那真是繁杂得很哪,反而引出更多的概念来
什么是mutable,什么是immutable
什么是stack,什么是heap
什么是变量,什么是对象本体
变量保存什么,对象本体保存什么
变量本身保存在哪儿,对象本体保存在哪儿
什么是地址,什么是引用
什么是传值,什么是传地址,什么是传引用
传的是谁的值,谁的地址,谁的引用
……
不画上几张图恐怕是很难解释详细,说不得还要讲讲x86体系
那可头疼死了,所以俺一开始就避重就轻,简单对比C/C++的情况
一直比较佩服buaawhl的认真态度


是啊,涉及的基本概念比较多。:-) 而这些都应该属于程序员的基本功、常识库的内容。
我只是借题发挥,针对一些共性的问题 发发牢骚和意见。
感觉很多程序员 被一些资料的用语 误导了,而且,自己也不愿意花时间在基本功上,去弄明白真正的工作原理,只愿意猜想。

总的来说,主要问题还是在于 目前的资料导向,避重就轻。大家都明白的东西,反复的说,大家都不明白的东西,它也不明白,也就不说。

我说自己好为人师,也是这个原因。我其实很不希望 重复一些本应该是常识的东西。只是看到,基本功常识没有得到应有的重视,反而让一些虚头巴脑的概念炒作 占了主流。题外话了。:-)
0 请登录后投票
   发表时间:2005-05-13  
幸亏此贴没有被 canonical 关注,不然他会把此问题引申到茶叶蛋和老婆饼上面

buaawhl 已经引申到了堆和堆栈了,离cpu体系不远了:)
0 请登录后投票
   发表时间:2005-05-13  
buaawhl 写道

我只是借题发挥,针对一些共性的问题 发发牢骚和意见。

其实俺也是感慨一下,如果大家对于某个公共的概念理解不同,交流起来是多么不容易啊
buaawhl 写道

总的来说,主要问题还是在于 目前的资料导向,避重就轻。大家都明白的东西,反复的说,大家都不明白的东西,它也不明白,也就不说。

缺乏细节
现在流行“只不过没告诉你全部真相”啊,呵呵。
0 请登录后投票
   发表时间:2005-05-15  
把java种的引用 当作 没有指针语法的指针就可以了。。。
应该只有 值传递。。。(个人感觉 地址传递就是值传递 只不过传的是地址而已 。。。绕口令。。。。。。。。。)

neooen 写道
幸亏此贴没有被 canonical 关注,不然他会把此问题引申到茶叶蛋和老婆饼上面

buaawhl 已经引申到了堆和堆栈了,离cpu体系不远了:)


...茶叶蛋和老婆饼??呵呵。。。zm 引伸出来???
0 请登录后投票
   发表时间:2005-05-16  
关于这个问题,我曾写过一篇文章专门进行讨论。我现在把它贴到了自己的blog上,哪位有兴趣不妨看一眼。
http://dreamhead.blogbus.com/logs/2005/05/1189478.html
0 请登录后投票
   发表时间:2005-05-17  
dreamhead 写道
关于这个问题,我曾写过一篇文章专门进行讨论。我现在把它贴到了自己的blog上,哪位有兴趣不妨看一眼。
http://dreamhead.blogbus.com/logs/2005/05/1189478.html


好文章。

dreamhead 写道

参数传递的秘密
知道方法参数如何传递吗?
记得刚开始学编程那会儿,老师教导,所谓参数,有形式参数和实际参数之分,参数列表中写的那些东西都叫形式参数,在实际调用的时候,它们会被实际参数所替代。
编译程序不可能知道每次调用的实际参数都是什么,于是写编译器的高手就出个办法,让实际参数按照一定顺序放到一个大家都可以找得到的地方,以此作为方法调用的一种约定。所谓“没有规矩,不成方圆”,有了这个规矩,大家协作起来就容易多了。这个公共数据区,现在编译器的选择通常是“栈”,而所谓的顺序就是形式参数声明的顺序。
显然,程序运行的过程中,作为实际参数的变量可能遍布于内存的各个位置,而并不一定要老老实实的呆在栈里。为了守“规矩”,程序只好将变量复制一份到栈中,也就是通常所说的将参数压入栈中。
打起精神,谜底就要揭晓了。
我刚才说什么来着?将变量复制一份到栈中,没错,“复制”!
这就是所谓的值传递。
C语言的旷世经典《The C Programming Language》开篇的第一章中,谈到实际参数时说,“在C中,所有函数的实际参数都是传‘值’的”。
马上会有人站出来,“错了,还有传地址,比如以指针传递就是传地址”。
不错,传指针就是传地址。在把指针视为地址的时候,是否考虑过这样一个问题,它也是一个变量。前面的讨论中说过了,参数传递必须要把参数压入栈中,作为地址的指针也不例外。所以,必须把这个指针也复制一份。函数中对于指针操作实际上是对于这个指针副本的操作。
Java的reference等于C的指针。所以,在Java的方法调用中,reference也要复制一份压入堆栈。在方法中对reference的操作就是对这个reference副本的操作。
谜底揭晓
好,让我们回到最初的问题上。
在changeReference中对于reference的赋值实际上是对这个reference的副本进行赋值,而对于reference的本尊没有产生丝毫的影响。
回到调用点,本尊醒来,它并不知道自己睡去的这段时间内发生过什么,所以只好当作什么都没发生过一般。就这样,副本消失了,在方法中对它的修改也就烟消云散了。

也许你会问出这样的问题,“听了你的解释,我反而对changeInteger感到迷惑了,既然是对于副本的操作,为什么changeInteger可以运作正常?”
呵呵,很有趣的大脑短路现象。
好,那我就用前面的说法解释一下changeInteger的运作。
所谓复制,其结果必然是副本完全等同于本尊。reference复制的结果必然是两个reference指向同一块内存空间。
虽然在方法中对于副本的操作并不会影响到本尊,但对内存空间的修改确实实实在在的。
回到调用点,虽然本尊依然不知道曾经发生过的一切,但它按照原来的方式访问内存的时候,取到的确是经过方法修改之后的内容。
于是方法可以把自己的影响扩展到方法之外。
0 请登录后投票
   发表时间:2005-05-17  
晕,被dreamhead一引申把reference的前生后世都搞出来了,lhlh pfpf

这个问题应该以语言对象化前后为分水岭来看

对象前类型:直接传值过去也可以,传值的指针过去也可以。(型参和值参是这时的概念,有指针的概念支持)。

对象后类型:对象在内存里就是一窝内存空间,传递这窝内存肯定是效率低下的了,只好传地址或叫引用过去。这时候就没有型参值参的概念了,因为没了指针概念,
所以后对象主义概念里面只有引用和值参。:)
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics