对比C++理解Java函数传参数的方式

《总结java方法(函数)传值和传引用的问题》帖子中总结了Java函数传参数的用法,其中提到“ java方法基本数据类型是传值,对象类型传引用”,个人认为这种说法不妥, Java对象类型传入的仍然是值,只不过是“对象引用的副本”,即把对象引用的值,而不是对象引用的引用传进去了 这样说太绕了,下面举个例子。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void Foo(String s) {  
  2.     s = new String("Hello");  
  3. }  

在函数外面new一个String:String s1 = new String("World"),然后执行 Foo(s1),然后输出s1的值是怎样的,我们都知道,当然还是“World”。但是既然传入的是引用,而不是值,那为什么执行完 Foo(s1) 之后,s1 的值没有变呢?我们再看下面这个C++的例子,从中寻找答案。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. class CObj {  
  6. public:  
  7.         CObj() {x = 0;}  
  8.         CObj(int x) {this->x = x;}  
  9.   
  10. public:  
  11.         int x;  
  12. };  
  13.   
  14. void Foo1(CObj * pObj) {  
  15.         pObj = new CObj(200);  
  16. }  
  17.   
  18. void Foo2(CObj * &pObj) {  
  19.         pObj = new CObj(200);  
  20. }  
  21.   
  22. int main(int argc, char * argv[]) {  
  23.         CObj * pObj1 = new CObj(100);  
  24.         cout << pObj1->x << endl;  
  25.   
  26.         Foo1(pObj1);  
  27.         cout << pObj1->x << endl;  
  28.   
  29.         Foo2(pObj1);  
  30.         cout << pObj1->x << endl;  
  31.   
  32.         return 0;  
  33. }  

运行结果如下:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 100  
  2. 100  
  3. 200  

这里的 Foo1() 函数就类似上面 Java 代码中的 Foo() 函数。Java中所谓的引用,可以理解成堆上生成的真正对象的地址,这里用 “C++中的指针”、“地址”、或是“引用”来表示都可以,总之,它本身不是对象的“肉身”,只是一个能找到“肉身”的标识。请注意,这样的一个标识,也有它的值,它的值就是它指向的对象肉身的地址。

C++代码中的 Foo1() 函数,传入的 pObj1 是一个标识的值,即 pObj1 虽然本身是一个指针,但是Foo1(pOjb1) 中,pObj1 是以“值传递”的方式传入的,说白了,就是真正传给了 Foo1() 函数的,是 pObj1 的一个副本,就相当于下面这样:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Foo1(CObj * pObj) {  
  2.     CObj * pTmp = pObj;  
  3.     pTmp = new CObj(200);  
  4. }  
可见,Foo1() 函数外面的 pObj1在Foo() 函数里是不会被改变的。这就说明了为什么下面的代码不能改变传入的String引用了。
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void Foo(String s) {  
  2.     s = new String("Hello");  
  3. }  
其实,打个比方,这段代码就相当于(当然不是很贴切,因为s本身就已经是一个local变量了):
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void Foo(String s) {  
  2.     String tmp = s;  
  3.     tmp = new String("Hello");  
  4. }  

相比之下,C++代码中的 Foo2() 函数,就能改变传入的 pObj 对象本身,因为传入的指针本身被重置了!

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Foo2(CObj * &pObj) {  
  2.         pObj = new CObj(200);  
  3. }  


上面各种方式的比较:


C++的传入引用貌似方便,但是常常要看到函数实现的内部,才知道传入的参数究竟在函数中是只读使用的,还是有写操作的。相比之下,Java基本上就是通过函数返回值传出结果,比C++要清晰的多。因此,在用Java时,尽量不要像C++的方式靠,而应尽量利用函数的返回值。比如,如果要同时返回两个String则可以返回一个List<String>;又比如,如果要将字符串“Jack_London_1234”中的字符串"London"和数字1234识别并返回,可以返回一个List<Object>,第一个元素是String "London",第二个元素是Integer 1234,然后分别做类型转换,转换成String类型和Integer类型。


PS


这篇帖子指出,JVM的引用,实际上就是标准的“对象指针”,根据不同的平台,表现为一个32位或64位的整型值,该整型值就是“对象指针”指向的对象在内存的地址。


PPS


如果String类提供一个函数,比如void assign(String input),该函数将input的内容赋给当前String对象(注意是堆上的对象实体),那么像下面这样的函数也是可以通过s参数返回“hello”的。之所以String类没有提供这样的函数,可能就是编写者不希望使用者像C++那样去用参数吧。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void Foo3(String s) {  
  2.     s.assign("hello");  
  3. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值