C#与java值传递区别解析

6 篇文章 0 订阅

首先要分清几个概念: “值类型”与“引用类型”;“值传递”与“引用传递”。以下是这几个概念的解释:
1 值类型: 存储在线程栈里的数据类型,如int型;
2 引用类型:存储在托管堆里的数据类型,如Date型,或自定义class对象;
3 值传递: 每次传递变量时,都是对栈里的原始值进行拷贝,如栈地址0001处存放一个整数值4,值传递时,先copy整数值4,将之存放在栈地址0002处的内存空间,再将栈地址0002进行传递;
4 引用传递: 每次传递变量时,直接传递栈地址,如栈地址0001处存放一个整数值4,引用传递时,直接传递栈地址0001,而不做复制。

        对于引用类型的变量而言,它们的“内容”存放在托管堆里,而在线程栈里存放的,是“内容”的“地址”。如对象在托管堆中的地址是x0001,那么栈地址0001所指向的内存空间中存放的就是这个对象的内存地址x0001(此处栈地址和托管堆地址并无任何对应关系,由操作系统进行分配)。

        由此大概可以推想,“数据类型”与“传递方式”并不是相关的,也就是说“值类型”并不一定对应“值传递”,“引用类型”并不一定对应“引用传递”。其实可能的组合有4种: 1 “值类型”进行“值传递”;2 “值类型”进行“引用传递”;3 “引用类型”进行“值传递”;4 “引用类型”进行“引用传递”。我们分别针对这四种情况进行说明:
       
1 这种情况是经常见到的。首先定义函数function ChangeValue(int value){ value = 10;} 。int a = 1;ChangeValue(a); Print(a).
        2 这种情况C#支持,而Java不支持。函数定义和调用都要稍作调整。具体如下:function ChangeValue(ref int value){ value = 10;} 。int a = 1;ChangeValue(ref a); Print(a).
        大家应该能猜到,1和2打印出来的结果分别是“1”和“10”。因为值传递,传递的是原始值的副本,副本值的改变并不会影响原始值;而引用传递,传递的是原始值的地址,所以改变的就是原始值本身(引用传递在C#中以ref或out关键字标识)


        对于引用类型而言,也是一样。例如:
        3 Person p = new Person(“Tony”);定义函数: function ChangeValue(Person p){Person new_p = new Person("John"); p = new_p;},调用 ChangeValue(p); Print(p.Name);

        4   Person p = new Person(“Tony”);定义函数: function ChangeValue(ref Person p){Person new_p = new Person("John"); p = new_p;},调用 ChangeValue(ref p); Print(p.Name);
         对于情况3,打印的结果还是“Tony”,因为传递的是一个副本,那么即使指向的对象变化了,也是副本的变化,与原值无关;对于情况4,使用了ref关键字,传递的是原始值的地址,所以指向的修改完全是针对原始值的,所以4的打印结果为“John”。

        有朋友可能会有疑惑: 因为Java只支持值传递,那么对于情况3里的引用类型Person来说,也只能进行值传递,那么修改的永远只能是副本,这样的话如何修改原对象的属性值呢?这岂不是出大乱子了?

         其实即使是值传递,也是可以正常修改原对象的属性值的。例如Person类对象p在托管堆里的地址是x0001,而该地址存放在栈地址0001对应的内存空间中,即0001->x0001,值传递时进行副本拷贝,拷贝值x0001到新的栈地址空间中,即0002->x0001,可见原始值和副本值都是x0001,即对象p在托管堆里的地址。所以我们定义函数ChangeName(Person p){p.Name = "John";}调用 Person p = new Person(“Tony”); ChangeName(p); Print(p.Name);打印的结果是"John"。因为函数中p.Name = "John",实际是x0001.Name = "John",即修改的是我们想要修改的对象p。

        因为Java只支持值传递,所以当传递一个引用类型给函数时,函数内部只能修改该对象的属性值,却不可以更改对象的指向本身,即如果引用指向对象p,函数内部是不能将引用指向换成new_p的;即使换了,换的也只是副本的指向而已。

        至于Java为何这样设计,我猜是基于安全性的考虑吧。当然c#中用ref和out标识引用传递,也不失为一种好的设计思路。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值