论坛首页 Java企业应用论坛

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

浏览 92910 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-04-07  
引用
由你的这句话就可以得出,Java中一切都是值传递。你可以通过方法来改变被引用的对象中的属性值,却无法改变这个对象引用(Object reference)本身.也就是当一个对象的实例被创建的时候,like this: Apple a = new Apple(); a 存的就是这个对象实例的地址。而这个地址,也就是a的值作为参数传到某个函数中的时候,a本身是不会改变的。


对,除非java中有所谓的引用的引用。

现在回忆起c程序中也有过类似的问题,c传递point也是传递point的拷贝,我也曾经误认为传递的point本身。

这个例子在c中是可以改一改正常使用的,因为c中存在指针的指针。
0 请登录后投票
   发表时间:2004-04-07  
我想,只要以前曾经有过C++基础的朋友们一定不会有这个疑惑的!
0 请登录后投票
   发表时间:2004-04-07  
曹管理员(想笑,敌后武工队里的曹大队长)说的好!但是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的插件吗?
0 请登录后投票
   发表时间:2004-04-07  
呵呵,你的层次显然是比我高了一层,的确,从java的虚拟机原理中肯定可以找到问题的实质的。
    我的确受了c++的影响,甚至说,我是受了编译原理的影响,其实,关于传引用还是传址还是传值,这只是一种概念,您提的那是实现罢了。不过最后一句关于不要在方法内部去改变外部传入对象的引用我非常受用,多谢了。
    这的确是一个非常简单的问题,看起来简单的可怕,但是它带来的影响也非常可怕,我相信很多人都能理解,甚至说出过所以然,但是绝对有很多人被它害了(也许程度不深罢了)。
    它不属于Java技术论坛区的任何一个区,换成robbin的说法,这样的问题,甚至死不应该在这里提的。不过,它能成为精华贴,我想有它的价值和道理的。
    最后,也多谢各位朋友。
    mikeho,我是在你问我的前两天偶然发现了这个问题,当时我有些疑惑,想,也许只是自己理解错误了。后来,你问我,再加上我尝试问过其他几个朋友,发现他们都理解错了。我才了解这个问题的重要性了。
0 请登录后投票
   发表时间:2004-04-07  
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的插件吗?


Java =有两个语意,一个是引用付值,一个是对象拷贝。
String的=被java重载过了,String=相当于对象拷贝。
String中有一个叫value的char数组。
a=b
在a内部会释放a自己的value,接管b中的value。因此a为hi.这个时候b中的value的reference已经被设置为空。

b="lover"的时候,将无名对象"lover"中的value付给b.因此b为lover.而a不变。
0 请登录后投票
   发表时间:2004-04-07  
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)
0 请登录后投票
   发表时间:2004-04-07  
凤舞凰扬 写道

    我的确受了c++的影响,甚至说,我是受了编译原理的影响,其实,关于传引用还是传址还是传值,这只是一种概念,您提的那是实现罢了。不过最后一句关于不要在方法内部去改变外部传入对象的引用我非常受用,多谢了。




其实,在java当中,的确是存在“引用”的,因为既然有堆,有变量,变量肯定保存的是堆中的地址,所以这肯定是一种“引用(reference)”关系 。我试图用Stack来解释的就是这个“传值”的概念。因为指向对象的变量保存的是对象的引用,所以在invoke的时候,这个引用被复制到被调用过程的堆栈中。

所以说,传地址还是传值,在java中是一个意思。地址就是引用,而引用就是一个整数值。原来所谓在C中的传地址和传值的不同,本质上是因为c中变量本身也是放在堆里面的,可以被引用才造成的。而在java中这个问题根本不存在,slot和堆是被隔离的。

所谓指针,在java中不存在这个概念。因为指针"pointer"本质上仍然是一个引用,只不过在c/c++中为了便于程序员操作,附加了操作方法和操作语义,甚至可以出现指向pointer的pointer(即二次引用)。而在Java中根本不允许直接操纵引用,所以也就不存在“pointer”一说,所有的一切都是针对堆中的引用。

ps: 恩,我说不应该放在java企业版,结果查了一下版面,好像也只有这个版和java语言沾点关系....那么放在这里也无不可了。
0 请登录后投票
   发表时间:2004-04-07  
其实这个问题更进一步,就会牵扯出static 变量的问题。嘿嘿.....

我只说一句:普通变量是保存在对象的方法的slot里面,而static变量是保存在类的方法区里面的。每个类只有一个方法区。
0 请登录后投票
   发表时间:2004-04-07  
slot在cpu寄存器里还是内存里,要是前者,

volatile关键字在同步操作时有使用的必要吗?




ps:你的书&lt;&lt;深入虚拟机&gt;&gt;哪里搞的到!
0 请登录后投票
   发表时间:2004-04-07  
说到受害者的问题....我觉得受害者应该只会是以前的C使用者.甚至于C++使用者都不会有问题.因为STL里面已经出现了类似java引用的概念,Vector之类都是采用引用的方式的.

其实java使用者的编程负担是相当轻的,第一不用管分配内存,第二不用管指针.只要每个方法都只针对自己的临时变量编程,只通过return返回结果,应该99%的人都不会遇到这个问题的.
0 请登录后投票
论坛首页 Java企业应用版

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