锁定老帖子 主题:一个绝对害了不少人的Java技术问题!
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-05-15
曹晓钢 写道 alin_ass 写道 曹管理员(想笑,敌后武工队里的曹大队长)说的好!但是t.setName()还是对被调用者堆栈做出了修改,这里可以称为引用吧?可是这个另人迷惑的词"引用到底在内存中是什么格式呢?为什么下面的代码会是这样结果呢?能不能更深入的讲讲"="这个操作符在内存中的运做呢?
/** * @author <a href="mailto:wanglin@japp.org">wang lin</a> */ public class PassByValue2 { public static void main(String[] args); { String b = "hi"; String a = "hello"; a = b; b = "love"; System.out.println("a: " + a);; } } 输出: a: hi 另问下用什么来看.class的字节码的,有eclipse的插件吗? 首先要区分出变量和对象的不同。对象是保存在堆中的,而变量是保存在对象instance的这个方法对应的临时slot中的。(slot有点像一个临时的寄存器)。因为每个方法都有自己的临时栈帧,所以这些变量不会相互影响,唯一的例外就是在方法调用的时候,由invokexxx和xreturn执行堆栈的初始化和反向更改。 那么,a="hi",这句话会在堆中产生一个String对象,编译器会在当前执行方法的对象的临时slot中产生一个slot号,比如说是1,这个变量slot中会保存这个String对象在堆中的地址。 a=b,其实就是一次压栈和一次出栈。 每个对象在堆都有一个地址,假若a(假设被编译器指定为slot0)指向的String对象"hello"是125,b(slot1) 指向的String 对象"hi"是126,那么a=b就是: aload_1; astore_0; 把'126'压入堆栈,然后弹出到slot0中,最后结果是slot0和slot1中都是126。 最后一句b="love",又在堆中产生了一个String对象,假若它的地址是127,则slot0被赋予127。 最终结果是: slot0 (a) = 126; slot1 (b) = 127. 然后125这个对象因为不再被引用,于是将可能在下次垃圾收集的时候被销毁。 这么说明白了吗? (察看字节码,请用javap命令行。好像是javap -c -verbose) 曹队长的解释果然详尽, 呵呵 不过这么详尽的解释未免杀鸡牛刀 以上代码改成 /** * @author <a href="mailto:wanglin@japp.org">wang lin</a> */ public class PassByValue2 { public static void main(String[] args) { String b = "hi"; String a = "hello"; a = b; b = new String("love"); System.out.println("a: " + a); } } 这里是新建了一个String对象, 并将b指向他, 此时a仍然指向"hi", 当然new String("love")这种方式是不推荐使用的, 这里只是为了表达 |
|
返回顶楼 | |
发表时间:2004-05-18
从黄总的发言中可以了解到,同望的知识传递真是可怜的弱啊
|
|
返回顶楼 | |
发表时间:2004-05-18
没有想到这个帖子还在继续,所以上来看看最后的网友谈论。
楼上,把我吓一跳!你是哪一位?老方或者......? 至于知识的传递,这在哪个公司都一样,不能说是具体个人的责任,主要在于许多公司没有培养这样的氛围,所以只有论坛才让大家可以交流。 |
|
返回顶楼 | |
发表时间:2004-05-19
呵呵,其实就是看到熟人,忍不住随便找条理由开个玩笑吓一下而已。
本来想将错就错的,不过为了避免他人名誉毁在我手里,还是要澄清一下。 此方非彼方。 如果有心的话,不难知道我是谁的,在这里就不说了。 |
|
返回顶楼 | |
发表时间:2004-11-06
《Practical Java》
的第一个实践就是这个了 |
|
返回顶楼 | |
发表时间:2004-11-08
曹队长的分析就是精辟
|
|
返回顶楼 | |
发表时间:2004-11-09
其实这个问题在java编程思想一书中有详细阐述,到底是传值还是传引用关键在于对值和引用的理解,希望各位老大在读高深读物的闲暇读一读java编程思想
|
|
返回顶楼 | |
发表时间:2004-11-10
记得2001年的一本书,书名已经不记得了,讲得非常好。
对于对象而言,方法参数实际上在堆栈中拷贝了一份句柄,这个句柄同外部句柄共同指向堆中的同一块内存空间。这就是所谓的传引用。 其实,我觉得大家都可以理解,只是看问题的角度不同而已。 |
|
返回顶楼 | |
发表时间:2004-11-15
同一个问题有几个版本,下面也是一种:
public class Foo { public static void main(String[] args);{ StringBuffer a=new StringBuffer("A");; StringBuffer b=new StringBuffer("B");; operate(a,b);; System.out.println(a+","+b);; } static void operate(StringBuffer x,StringBuffer y);{ x.append (y);; y=x; } } ------------------------------- 如果把上面的代码翻译成下面的,就很清楚了 StringBuffer a=new StringBuffer("A");; StringBuffer b=new StringBuffer("B");; { StringBuffer x=a; StringBuffer y=b; x.append(y);; //same as : a.appen(y);; y=x; //x=null; //y=null; } System.out.println(a+","+b);; ------------------------------- 凤舞凰扬 写道 我不记得自己是怎么学到的,但是我相信绝大部分从事java学习Java的人都一直这么以为一个小的技术问题:
在Java中方法参数的传递,对象是传递引用,基本数据类型是传递值。而且一直一来都似乎没有人提出过疑问。 直到最近,我在为公司基本Java开发人员编写考试试卷的时候,我才发现,这错了!在方法中,Java语言中对象传递的是地址,而不是引用,这两个概念是有非常大的差别的,我相信熟悉c++的人都应该知道。 例如下面:假设对象Test有name的属性。 public void call(Test t) { Test t2 = new Test(); t2.setName("cba'); t.setName("abc"); t = t2 ; } public static void main(String[] arg) { Test obj = new Test(); call (obj) ; System.out.println("obj"+obj.getName()); } 这个时候,你们可以发现,打印出来的是"abc" ,而不是"cba",原因是这样的,在这次调用中,等于声明了两个变量obj , t,它们指向的是同一个地址,调用call方法,只是将obj指向的地址传递给了t,而obj本身并没有传递过去(也就是没有传递引用),当你重新赋值的时候(也就是将对象引用指向其他存储空间),等于只影响了t,而没有影响obj。 这样的传递方式只能称之为址传递,或者是引用对象传递,而不嫩说是传递引用或者引用传递。 我不知道这究竟属于翻译的错误,还是我们理解的错误。但是这样的问题在c++中是有明显的区分的(通过*与&) |
|
返回顶楼 | |
发表时间:2004-11-23
分析如下图
wuweixian 写道 Java只有一种参数传递方式,那就是传值。
证明如下: //Test.java class Test { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } //TestTransferParameter.java public class TestTransferParameter{ public static void main(String[] args) { Test a = new Test(); a.setName("a"); Test b = new Test(); b.setName("b"); System.out.println("before swap: " + "a=" + a.getName() + "; b=" + b.getName()); swap(a,b); System.out.println("after swap: " + "a=" + a.getName() + "; b=" + b.getName()); } private static void swap(Test a, Test b) { Test temp; temp = a; a = b; b = temp; System.out.println("swaping: " + "a=" + a.getName() + "; b=" + b.getName()); } } 输出结果: before swap: a=a; b=b swaping: a=b; b=a after swap: a=a; b=b 如果用基本数据类型比如int代替上面的a,b,比如a=100;b=0; 输出结果: before swap: a=100; b=0 swaping: a=0; b=100 after swap: a=100; b=0 现在很清楚了,Java只有一种参数传递方式,那就是传值。 附:《编译原理》中介绍的四种参数传递方式: 1、传地址 call by reference 2、传值 call by value 3、传名 call by name 4、得结果 call by result 以上是个人的一点理解,有不同意见欢迎探讨! |
|
返回顶楼 | |