论坛首页 Java企业应用论坛

一个绝对害了不少人的Java技术问题!

浏览 92915 次
该帖已经被评为精华帖
作者 正文
   发表时间: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")这种方式是不推荐使用的, 这里只是为了表达
0 请登录后投票
   发表时间:2004-05-18  
从黄总的发言中可以了解到,同望的知识传递真是可怜的弱啊
0 请登录后投票
   发表时间:2004-05-18  
没有想到这个帖子还在继续,所以上来看看最后的网友谈论。
   楼上,把我吓一跳!你是哪一位?老方或者......?
    至于知识的传递,这在哪个公司都一样,不能说是具体个人的责任,主要在于许多公司没有培养这样的氛围,所以只有论坛才让大家可以交流。
0 请登录后投票
   发表时间:2004-05-19  
呵呵,其实就是看到熟人,忍不住随便找条理由开个玩笑吓一下而已。
本来想将错就错的,不过为了避免他人名誉毁在我手里,还是要澄清一下。 此方非彼方。
如果有心的话,不难知道我是谁的,在这里就不说了。
0 请登录后投票
   发表时间:2004-11-06  
《Practical Java》
的第一个实践就是这个了
0 请登录后投票
   发表时间:2004-11-08  
曹队长的分析就是精辟
0 请登录后投票
   发表时间:2004-11-09  
其实这个问题在java编程思想一书中有详细阐述,到底是传值还是传引用关键在于对值和引用的理解,希望各位老大在读高深读物的闲暇读一读java编程思想
0 请登录后投票
   发表时间:2004-11-10  
记得2001年的一本书,书名已经不记得了,讲得非常好。
对于对象而言,方法参数实际上在堆栈中拷贝了一份句柄,这个句柄同外部句柄共同指向堆中的同一块内存空间。这就是所谓的传引用。
其实,我觉得大家都可以理解,只是看问题的角度不同而已。
0 请登录后投票
   发表时间: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++中是有明显的区分的(通过*与&)
0 请登录后投票
   发表时间: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

以上是个人的一点理解,有不同意见欢迎探讨!
0 请登录后投票
论坛首页 Java企业应用版

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