论坛首页 Java企业应用论坛

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

浏览 92909 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-04-06  
我不记得自己是怎么学到的,但是我相信绝大部分从事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-04-06  
不是call的缘故, 而是 t = t2的缘故
0 请登录后投票
   发表时间:2004-04-06  
JAVA里面的引用实际上就是指针,不象C++中引用与指针是两个概念。
0 请登录后投票
   发表时间:2004-04-06  
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 请登录后投票
   发表时间:2004-04-06  
现在很清楚了,Java只有一种参数传递方式,那就是传值。
---------------------------
那怎么解释这种情况:
public modify(Test t) {
t.setName("222")
}

class Test(){
string name = "111";
public setName...;
}


main(){
Test test = new Test();
System.out.println(test.getName() );
modify(test);
System.out.println(test.getName() );
}

打印的结果是111 222
可见在这里例子中是传引用,不是传值。

从语言设计的角度来说,不可能设计成只能传值。
楼上的和楼主的例子确实有些奇怪。。。。
0 请登录后投票
   发表时间:2004-04-06  
<%!
private void modify(Book b);{
	
	System.out.println("### begin modify");;

	System.out.println("the ref of b is:" + b);;
	Book book2 = new Book();;
	book2.setBookName("222");;
	System.out.println("the ref of book2 is:" + book2);;
	b = book2;
	System.out.println("after assign value,the ref of b is:" + b);;
	System.out.println("b.getName();:" + b.getBookName(););;
	
	System.out.println("### end modify");;
}
%>

<%
	Book book = new Book();;
	book.setBookName("1111");;
	System.out.println("--- before modify");;
	System.out.println("book is:"+book);;
	System.out.println("bookName is:"+book.getBookName(););;
	modify(book);;
	System.out.println("--- after modify");;
	System.out.println("book is:"+book);;
	System.out.println("booName is:"+book.getBookName(););;
%>


输出结果:
 --- before modify
 book is:com.jaqsoft.demo.Book@765f2360
 bookName is:1111
### begin modify
 the ref of b is:com.jaqsoft.demo.Book@765f2360
 the ref of book2 is:com.jaqsoft.demo.Book@77866360
 after assign value,the ref of b :com.jaqsoft.demo.Book@77866360
b.getName();:222
### end modify
 --- after modify
 book is:com.jaqsoft.demo.Book@765f2360
 booName is:1111
0 请登录后投票
   发表时间:2004-04-06  
从上面的例子很明显的看出,对象调用绝对传递的是对象的引用,而非对象的拷贝!-在book对象传进去之后它的ref值没有改变。

而将新的对象的引用赋值给book,在函数内book的引用确实改变了,所以能打印出改变后的结果。

但是脱离函数之后,book的引用为什么又回到了赋值前的那个呢???
--这才是引入误会的地方,这个地方直接导致了误会:传值。

现在的问题是为什么book的引用在脱离函数后,为什么会回复到以前的值??
按照常规理解应该是,赋值后,book将一直指向book2所引用的对象,而book以前所指向的对象则列入垃圾收集的范畴。

谁能解释一下这个奇怪的现象??
0 请登录后投票
   发表时间:2004-04-06  
jaqwolf 写道
<%!
private void modify(Book b);{
	
	System.out.println("### begin modify");;

	System.out.println("the ref of b is:" + b);;
	Book book2 = new Book();;
	book2.setBookName("222");;
	System.out.println("the ref of book2 is:" + book2);;
	b = book2; 
———————————————————— 问题出在这。




至于为什么?想想应该不难得出结论。

我是这样理解的。程序里会有对象以及对象的引用。对象就是占用内存比较大的东西,引用就是占用内存比较小的东西,可以看作是C里的指针。Java里的传值实际上是拷贝引用,而不是拷贝对象。

所以不管引用怎么变,只要不调用引用的方法,对象的任何属性都不会变。
0 请登录后投票
   发表时间:2004-04-06  
感觉上是作为参数的句柄在方法内部是受到一定的保护,当直接获得该句柄所指
的内存空间进行操作时能顺利进行,如:
   public void modify(Object obj);{
    
       Object newobj = new Object();;
       newobj = obj ;
       newobj. .....;

  }


但如果想将参数句柄指向另外一个内存空间的时候,在方法内部的操作结果只在方法内部有效,返回后该句柄还是回到原来所指向的内存空间,如:
   public void modify(Object obj);{
    
       Object newobj = new Object();;
       obj = newobj ;
       obj. .....;

  }



小弟愚见,希望高手指正
0 请登录后投票
   发表时间:2004-04-06  
引用
Java里的传值实际上是拷贝引用,而不是拷贝对象。


OK,一语中第。
java传递是引用拷贝,既不是引用本身,更不是对象。
所有的问题都结了。
1 请登录后投票
论坛首页 Java企业应用版

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