c java 传参数_传值还是传引用—— java和C函数参数传递解读

关于Pass-By-refrence 和Pass-By-value 的争论始终活跃在论坛一线。

今天就来揭露其中的来龙去脉, 给自己和初学者来个彻底剖析。

首先来看, 为什么有"值"和"引用"的提法.

什么是值(Value), 直接操作的东西, 就是值。如我有一个包裹, 不用通过中间手段, 我拿起来就可以操作。

相对于引用(refrence), 是指间接的东西。比如我有一个包裹的传单, 当然要通过这个传单上的包裹地址,才能找到包裹,进而去操作。

reference, 就是中介的意思,指引你去找到你需要的对象(值). reference除了帮你找到你需要的对象之外,没有其它作用。

在Java中, 没有“对象(值)”,之说,你看到的,都是对象的reference,并且都是在堆中创建(基本类型除外), 比如:

Object obj = new Object(); //创建一个对象(在堆中), 并将obj引用指向这个对象。

不像C++,除了上述方法之外,还可以这样创建:

Object obj(); //创建一个对象(在栈中), obj是一个实实在在的对象

因此,java中,明确表示,java中,只有引用的概念。

现在来看参数传递的原理:

定义一个函数 void fuction(A a)

这里A可以指代任何变量, 指针,值或者是其它。

调用时:

A b;

fuction(b);

发生了什么事情?

首先b将自己复制一份b1,并将b1作为形参,给fuction的入口值a.

所有的函数传递务必都是这样,但是,当形参如果reference和value的时候, 我们会有一些混淆:

1. 当为value传递的时候, 实参会复制一份, 比如用值的传了一个包裹到fuction中, 这个时候,包裹被复制, 所以包裹不是原来的包裹,此时,改写包裹内容, 对原来的包裹没有任何作用了!

2. 当为reference传递的时候,如前所述,此时传递的是带有包裹地址的传单, 这个时候传单复制了一份, 相当于复印件。 我们的操作,都是通过复印件传单的地址,去找到包裹,然后操作。复印件上的地址,和原来的地址,肯定是一样的,所以,我们找的,依然是原来的包裹对象,改写包裹,当然会对原来的包裹产生影响。

所以,函数传参, 都是值,引用值或者对象值

。如果想修改对象,传引用进去,这样方便找到对象然后去修改。

为什么要复制一份呢! 原因是每个函数是一个栈结构,栈空间管理变量资源,所以就有复制一说。

鉴于复制的原因,所以,传参数的时候,还是传引用比较好,大的对象会非常消耗栈空间的。

前些天论坛有个有趣的例子: 抄摘过来:

public static void main(String[] args)

{

StringBuffer str1 = new StringBuffer("hello");

test(str1);

System.out.println("main : " + str1);

}

public static void test(StringBuffer str)

{

StringBuffer tempStr = new StringBuffer();

System.out.println("first : " + str);

str = str.append(" world");

System.out.println("second: " + str);

str = tempStr;

System.out.println("third : " + str);

}

结果是这样的:

结果为:

first : hello

second: hello world

third :

main : hello world

把我的回复粘贴一把:

Java codepublicstaticvoid main(String[] args)

{

StringBuffer str1=new StringBuffer("hello");

test(str1);

System.out.println("main :"+ str1);

}

//传入的是一个StringBuffer对象的指针(java中喜欢称为引用)

//实际是: 将new StringBuffer("hello");的指针值,复制一份, 传递给str

publicstaticvoid test(StringBuffer str)

{

//到这里,注意,你拿的是new StringBuffer("hello")指针值的副本!叫做str

StringBuffer tempStr=new StringBuffer();

System.out.println("first :"+ str);

//通过str指针,先原来的对象并改变, 所以,你能改变原对象的值

str= str.append(" world");

System.out.println("second:"+ str);

//这里将副本改写,操作的不是对象,是副本的值,当然对原对象不起作用!

str= tempStr;

System.out.println("third :"+ str);

}

举一个例子:

有人从邮局给你送了一个包裹(对象new StringBuffer("hello")), 然后通过一个凭据(StringBuffer str1)写明包裹所在的邮局地址(包裹的引用地址),同时将这个凭据复印了一下(StringBuffer str), , 让你操作一下(test函数),撕开包裹,取出邮件。

重点讲一下test函数,即你拿到凭据复印件str之后,怎么操作的,

你通过str到地址邮局中找到了包裹, 打开它,在里面塞了一些废纸,

然后,你自己又弄了一个新包裹, 在里面塞了一只烟灰缸。这个时候,你把地址的复印单改成了新包裹的地址。

于是最后结果: 你原来的包裹, 里面有废纸,

新的包裹,里面有烟灰缸;

但很糟糕的是, 你私自改了包裹的复印单(生活中,复印单一般是不允许更改的,更改就无效),把它改成你新包裹的地址,

但你没有返回这个改了的复印单,你把它遗忘了, 所以处理完包裹之后,带有烟灰缸的包裹地址,你找不到了!

最后,你只能拥有原来的包裹,因为原来的包裹地址(包裹的地址的原件)邮局还为你保留着呢!

顺便提及,这里如果想得到带有烟灰缸的新包裹, 给一个返回值就OK了; 但似乎这并不是一个好的解决方案。

为了防止改写复印件, c++传递指针的时候, 用了关键词Constant, java则用了final,

异曲同工.

分享到:

18e900b8666ce6f233d25ec02f95ee59.png

72dd548719f0ace4d5f9bca64e1d7715.png

2009-10-03 23:09

浏览 353

评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值