`
lancezhcj
  • 浏览: 48756 次
  • 性别: Icon_minigender_2
  • 来自: 衡阳
社区版块
存档分类
最新评论

问题整理

阅读更多
看一下下面的例子:
public class Pass {
String a="123";

public static void test(Pass passA) {
passA.a="abc";
}
public static void main(String[] args) {
Pass passB=new Pass();
passB.a= "123";
System.out.println(passB.a);
test(passB);
System.out.println(passB.a);
}
}
结果是:
123
abc
从这个结果来看是通过引用传递的.String不是简单类型,那么是不是也是通过引用来传递呢?看下面这个例子:
public class Pass {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
System.out.println(string);
test(string);
System.out.println(string);
}
}
结果是:
Hello
Hello
第三个例子:
public class Pass {
public static void test(StringBuffer str) {
str.append("World");
}
public static void main(String[] args) {
StringBuffer string = new StringBuffer("Hello");
System.out.println(string);
test(string);
System.out.println(string);
}
}
结果:
Hello
HelloWorld

问:--java传值还是传引用?


buaawhl 写道
对于这个问题,如果学过汇编,那么就很容易从堆栈内存结构上理解。
http://dev.csdn.net/article/17/17286.shtm
楼主的第一段代码。
Java代码
public static void test(Pass passA); {   
passA.a="abc";   
}  

public static void test(Pass passA); {
passA.a="abc";
}


这个传的 PassA 的 地址值。这个 地址值 被复制了一份。
不信,你写:
Java代码
public static void test(Pass passA); {   
passA = null;  
}  

public static void test(Pass passA); {
passA = null;
}


看看, 对passA有什么影响?
毫无作用。函数调用出来后,passA还是原来的值,不会变成Null.

但是,你的代码对 passA进行了操作  passA.a ,改变了passA的成员变量。
这个成员变量是一个真实指向String 的 地址,当然能够被改变。
这就是操作 (.)  和 赋值 (=) 的区别。
这是对 成员变量 a 的 赋值。真正改变了成员变量 a 的值。

注意,这里传递的参数是 passA, 而不是 a.
所以,passA 被复制了一份。passA 的这个副本的 a 变量还 指向 原来的 passA 的 a 变量。

楼主后面的代码,
Java代码
public static void test(String str); {   
str = "World";   
}  

public static void test(String str); {
str = "World";
}


只有对参数的 赋值,没有对参数的操作,当然不会产生影响。


引用

public static void test(StringBuffer str) {
str.append("World");
}



对参数进行了操作,当然会产生影响。




楼主的这个问题 主要是 参数传递的问题。
这里涉及到的基本功常识为:
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 放到寄存器里面。


厌倦发呆 写道

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


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

dreamhead 写道
关于这个问题,我曾写过一篇文章专门进行讨论。我现在把它贴到了自己的blog上,哪位有兴趣不妨看一眼。
http://dreamhead.blogbus.com/logs/2005/05/1189478.html

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

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

wfeng007 写道
把java种的引用 当作 没有指针语法的指针就可以了。。。
应该只有 值传递。。。(个人感觉 地址传递就是值传递 只不过传的是地址而已 。。。绕口令。。。。。。。。。)


mginobili (架构师)因为在java中

一些基础类型int char long这些在调用方法时是按照值传递的

而对象则是按照引用传递的

字符串虽然是对象,但是String是被特殊处理过的,传入方法时只不过是这个String对象的拷贝
真正的String对象已经创建 是不可修改的

Java代码
String a = "abc";//创建了一个String对象a,内容为"abc"  
a = "b";//新创建了一个String对象,内容为"b",把这个对象的引用赋给了a 


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics