锁定老帖子 主题:一个绝对害了不少人的Java技术问题!
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-04-07
ozzzzzz 写道 指针在我看来是c最不成功的设计,很难理解为什么要用这个蹩脚的东西。实际上对于数据的传递如果用汇编级的分析就更容易明白。
在x86下,传值(真正的简单类型数据)现代编译器都是利用寄存器,这点在jvm中是无法实现的,没有办法只有一个鸟相当IP的寄存器。我不是玩这个把戏很久了,以后也不想玩这个寄存器。于是就只好用堆栈。 而传递对象,在x86下有两种情况。一种是把对象当作一组数据用堆栈来传递,这个时候你可能会pop,也可能利用BP。但是堆栈总是一个暂时的存储场所。 有的时候你会对象在数据区划分了一块地方,你传递的是它的地址。同时如果你使用堆栈而应用BP,你一样会传递它的地址或者偏移量。 这些清晰的概念在c下被搞的乌七八糟,鬼晓得是如何作的。 C里面的指针对于传递对象数据这个功能是用的很少的,更多的是用于直接访问直接访问内存,另外我上面那个帖子说了。在c里面指针还用作数组的random itereator.传递对象数据的作用在C++里面才比较明显。至于说的那种乱七八糟的情况,是来自于C++既允许堆对象,又允许堆栈对象。至于堆栈对象的好坏与否很难说了. |
|
返回顶楼 | |
发表时间:2004-04-07
:lol: 原来曹兄的意思和我一样,呵呵。说白了,就是对这个引用的概念上的理解偏差。
曹兄没有受C++的影响,因为java中没有地址的概念,呵呵,但是在Java中的传引用和C++中的传引用是不同的,倒是和C++中的传地址一样。 呵呵,所以我就说是传地址了(当然这个地址是虚地址罢了,不是内存中的地址,而是虚拟机中某块数据的存放位置)。 到了现在,大家总算殊途同归了! ,值得高兴的事情~~~~~~~, 我也难得有张帖子一下子就这么热烈,成了精华贴,哈哈,也多谢各位朋友参与其中了~~~~~~~~~~ |
|
返回顶楼 | |
发表时间:2004-04-07
你可以通过方法来改变被引用的对象中的属性值,却无法改变这个对象引用(Object reference)本身.也就是当一个对象的实例被创建的时候,like this: Apple a = new Apple(); a 存的就是这个对象实例的地址。而这个地址,也就是a的值作为参数传到某个函数中的时候,a本身是不会改变的。
|
|
返回顶楼 | |
发表时间:2004-04-07
Trustno1说得cpp的对象带来了混乱是说到点子上了,在java中就没有这个的问题,在asm中也没有这样的问题。c语言被称为中级语言,cpp我看也是中级语言。传值、传址、传句柄,我看不出在java中会成为coder的负担。而对于c中的指针操作内存,我觉得还是做的不好,干脆就把堆栈指针和内存指针分开。
而这些问题我们看来似乎不会对实际的工作带来太多的影响,就如凤舞飞扬说的很多人不清楚,可是工作一样继续。但是我觉得这对今后虚拟机的发展会有很大的影响。perl的虚拟机将使用寄存器,不知道这和堆栈式的jvm比较起来谁的效果会更好。其实IBM对于脚本语言的支持一直很看重,这一点从IBM的DW上就可以看出来。如果当PERL或者PYTHON的虚拟机技术成熟,我想IBM很可能会把现在对于java的投入转移到这些新东西上来。这些软件更加符合ibm的unix/linux的利益。 |
|
返回顶楼 | |
发表时间:2004-04-07
Trustno1 写道 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不变。 Trustno1这里的解释有些问题。实际上,String的=被重载的说法不成立。java是不支持操作符重载的。 实际上=就是一次简单的赋值操作。和Strin中叫做value的char数组无关。 |
|
返回顶楼 | |
发表时间:2004-04-07
ozzzzzz 写道 Trustno1说得cpp的对象带来了混乱是说到点子上了,在java中就没有这个的问题,在asm中也没有这样的问题。c语言被称为中级语言,cpp我看也是中级语言。传值、传址、传句柄,我看不出在java中会成为coder的负担。而对于c中的指针操作内存,我觉得还是做的不好,干脆就把堆栈指针和内存指针分开。
而这些问题我们看来似乎不会对实际的工作带来太多的影响,就如凤舞飞扬说的很多人不清楚,可是工作一样继续。但是我觉得这对今后虚拟机的发展会有很大的影响。perl的虚拟机将使用寄存器,不知道这和堆栈式的jvm比较起来谁的效果会更好。其实IBM对于脚本语言的支持一直很看重,这一点从IBM的DW上就可以看出来。如果当PERL或者PYTHON的虚拟机技术成熟,我想IBM很可能会把现在对于java的投入转移到这些新东西上来。这些软件更加符合ibm的unix/linux的利益。 速度,速度。这恐怕是最关键的问题,栈对象和堆对象分开当然是可以的在rtjava中的scopememory就是这样处理的。当然C#也是这样,不过C#的栈对象不在退栈后析构罢了。至于CPP么除非引入GC,否则就没有必要了。至于CPP为何要采用栈对象,主要的问题是引用语意需要GC才能处理的干净,详细的分析可以去看BJ的一些论文,这里不是一两句话能说清楚地 Python,ruby,这样的语言仰扛着两面大旗。1.无类型的动态语言.Java够OO了吧 ,但是Python,ruby可要比Java还OO。没有类型,甚至类型都可以动态的加以改变和操作(这有点像C++,不过C++依靠编译器进行模板推导,python依靠动态类型)。原本Java很多罗罗嗦嗦的代码比如refelcting,在Python里面2-3句就能解决。我曾经用Pyton写过一个O/R mapping的,大约只有10行左右的样子。 2.与C/C++无缝集成。python,ruby这样的语言甚至要比java还要慢10倍。 但是他们根本不考虑虚拟机的效率提升。python发展到现在version2.3,虚拟机的效率几乎没有本质的提高,甚至连一个JIT都没有。因为python很大方的把性能问题留给了c/C++,因此boost库里面就出现了一个python的子库。Python的集成可能还需要swig这样的工具,Ruby的接口简直就是无缝的(类似于emacs lisp和c)。而Java在这点上却是太过于吝啬了。 |
|
返回顶楼 | |
发表时间:2004-04-07
java虚拟机规范中这样写道:
java虚拟机操作在两种类型之上:基本类型和引用类型,相应的有两种值,可以存储在变量中,作为参数传递,被方法返回,以及对其进行操作:基本值和引用值。 java虚拟机包含对对象的显示支持,对象或者是一个动态分配的类实例,或者是一个数组。对象的引用被认为具有java虚拟机类型reference,类型reference的值可被看成指向对象的指针。一个对象可以有多个引用。 尽管java虚拟机对对象进行操作,但它从来不对它们直接寻址,对对象的操作、传递和检查总是通过类型reference的值进行。 有三种refrence类型:类类型、接口类型和数组类型。它们的值是动态创建的类实例、数组或者实现接口的类实例或数组的引用,reference的值也可以是特殊的null引用,它不引用对象。 java虚拟机不为对象要求任何特殊的内部结构,在sun的当前java虚拟机实现中,对类实例的引用是指向一个句柄的指针,这个句柄自身是一对指针:一个指向包含该对象的方法和代表该对象类型的class对象的指针的表,另一个指向java堆中为该对象数据分配的存储器,但是这只是sun用方法调度表技术实现java虚拟机,当然可以有别的实现方法,不用句柄。 ----------------------------------------------------------------------------------- 书太大了,传不上来。 |
|
返回顶楼 | |
发表时间:2004-04-07
就如同Trustno1所说,python不要求速度,把速度留给c去完成。而python可以说是给cpp的coder定制的,ruby大概也是这样。java的问题就在于不够开放,当然这是sun的问题。是sun对于ibm的敌意让java的发展遇到了问题。而如果大家关注一些python,就会发现在java社区有影响的开源项目python大多都有类似的对应,或者干脆就是移值。当然对于OR项目python会比较的少,多数都是xml的方面的。python还有一个巨无霸的zope,马上就要zope3。而且python社区比较团结,发展的方向统一,基本上都是围绕zope来进行商业开发。java的问题就刚好显现在这里,太多的利益不同。
|
|
返回顶楼 | |
发表时间:2004-04-07
wuweixian 写道 Java只有一种参数传递方式,那就是传值。
实际上站在硬件的立场上来看,所有的参数都是传值。编程语言里面的传值和传引用语法只是用来引导编译器根据语义来产生具有预期结果的不同指令序列而已。所以这些概念只是拿来影响编译器的,因此如果站在硬件的立场上来编写测试代码,被编译器隐藏的底层现实就会被揭示出来。 这样一来,可能在概念上会出现不小的混淆:到底那个才是对的?我们编程的时候如果遵循编程语言期望我们采用的思考方式,那么我们就是和硬件底层隔离的,那么传值和传引用就是不一样的;如果我们需要接触硬件,那么实际上就是背离了高级编程语言的初衷,我们就是站在硬件的角度来思考问题的,这样传值和传引用在本质上就是一样的。 |
|
返回顶楼 | |
发表时间:2004-04-11
看了大家这么热烈地讨论这个问题, 我想就我自己从C/C++到Java的学习经验来说上两句.
在C++中, 其实Reference和单级的Pointer(MyClass* ptr, not MyClass** ptr)在使用上的效果是一致的. 但是在Java中由于不让程序员直接操控内存空间, 所以只有了Reference, 去掉了指针. 这样, 下面这段Java代码的结果是显而易见的. 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(););; } 只要用C/C++中的指针概念解释一下就可以了. 把引用当成指针, 个人认为这样更好理解, 而且他们本来就是兄弟. 假设new Test()产生一个对象, 它的地址为100. 那么obj作为对Test一个对象的引用, 将被赋值为100. call(Test t)中的参数t在实际调用中将会被赋值为obj所指对象的地址的一个Copy, 仅仅是地址的Copy(这是和Pass By Value的区别), 也就是100. 当你在call中通过一个引用t调用Test相关的方法或者给其成员变量赋值, 这都将这将直接影响到t所指的对象的状态, 也就是main()中的obj所指的对象, 因为它们指向同一个对象. 但是你给call()中的t重新赋另一个对象的地址(t2, t=t2)时, t仅仅时指向了另一个对象. 但是为什么main()中的obj也要指向t2所指的对象呢??? 这是没有理由的. 所以t=t2不会对引用obj产生任何影响, obj还是指向地址为100的那个对象. 在C/C++中如过要达到楼主预期的结果只有使用指向指针存储空间的指针(MyClass** ptr). 这种技巧在C/C++的算法中很常见的. 我就有一次为了将C中的一个平衡树算法移植到Java上不得不让一个方法返回更新后的引用. public Node rotateRight(Node node); { ... ... // node = node.getRightChild();; No effect !!!!! return node.getRightChild();; } // Invocation root = rotateRight(root);; 不好意思, 是边听Linkin Park边写的, 不知道有没有讲清楚. 最后说一句, 这个问题的确会没有使用过C指针的程序员一点迷惑. 所以说对编译原理和稍微低级一点的语言需要做一些必要的了解.[/img] |
|
返回顶楼 | |