传递引用类型参数的两种方式(转自 MSDN)
引用类型的变量不直接包含其数据;它包含的是对其数据的引用。当通过值传递引用类型的参数时,有可能更改引用所指向的数据,如某类成员的值(更改属性的值),但是无法更改引用本身的值;也就是说,不能使用相同的引用为新类分配内存(比如在被调用的方法中通过new来分配新的内存空间)并使之在块外(调用方法中,比如Main方法中)保持。若要这样做,应使用引用传递方式(注意:引用传递方式和引用类型是不同的概念)——用 ref 或 out 关键字传递参数(参数类型可以是值类型也可以是引用类型)。为了简单起见,下面的示例使用 ref。
通过值传递引用类型
下面的示例演示通过值向 Change 方法传递引用类型的参数 arr。由于该参数是对 arr 的引用,所以有可能更改数组元素的值。但是,尝试将参数重新分配到不同的内存位置时,该操作仅在方法内有效,并不影响原始变量 arr。
class PassingRefByVal
{
static void Change(int[] pArray)
{
pArray[] = ; // 这里赋值 会改变 原来pArray[0]的值
pArray = new int[] { -, -, -, -, - }; // 这里的赋值,只在该方法内有效
System.Console.WriteLine("在Change方法里面,arr第一个元素的值是:{0}", pArray[]); pArray[] = ; // 这里赋值 不会改变 原来pArray[1]的值,只是改变这里新创建的数组的第一个元素的值
System.Console.WriteLine("在Change方法里面,arr第二个元素的值是:{0}", pArray[]);
} static void Main()
{
int[] arr = { , , };
System.Console.WriteLine("在Main方法里面,调用Change方法前,arr数组中第一个元素的值是:{0}", arr[]);
System.Console.WriteLine("在Main方法里面,调用Change方法前,arr数组中第二个元素的值是:{0}", arr[]); Change(arr);
System.Console.WriteLine("在Main方法里面,调用Change方法后,第一个元素的值是:{0}", arr[]);
System.Console.WriteLine("在Main方法里面,调用Change方法后,第二个元素的值是:{0}", arr[]); System.Console.ReadLine();
}
} /* 输出:
在Main方法中,调用Change方法之前,pArray中第一个元素的值是:1
在Main方法中,调用Change方法之前,pArray中第二个元素的值是:4
在Change方法中,pArray中第一个元素的值是:-3
在Change方法中,pArray中第二个元素的值是:666
在Main方法中,调用Change方法后,pArray中第一个元素的值是:888
在Main方法中,调用Change方法后,pArray中第二个元素的值是:4
*/
在上面的示例中,数组 arr 为引用类型,在未使用 ref 参数的情况下传递给方法。在此情况下,将向方法传递 指向 arr 的引用的一个副本 。输出显示方法有可能更改数组元素的内容,在这种情况下,从 1 改为 888。但是,在 Change 方法内使用 new 运算符来分配新的内存空间,将使变量 pArray 引用新的数组。因此,这之后的任何更改都不会影响原始数组 arr(它是在 Main 内创建的)。实际上,本示例中创建了两个数组,一个在 Main 内,一个在 Change 方法内。
下面的示例与前面的示例相比,除了在方法标头和调用方法的地方添加了 ref 关键字之外,其他的都一样。在方法中做的更改会影响到原始的变量。
class PassingRefByRef
{
static void Change(ref int[] pArray)
{
// 下面的更改都会影响到变量的原始值
pArray[] = ;
pArray = new int[] { -, -, -, -, - };
System.Console.WriteLine("在Change方法中,pArray中第一个元素的值是:{0}", pArray[]);
} static void Main()
{
int[] arr = { , , };
System.Console.WriteLine("在Main方法中,调用Change方法之前,pArray中第一个元素的值是:{0}", arr[]); Change(ref arr);
System.Console.WriteLine("在Main方法中,调用Change方法后,pArray中第一个元素的值是:{0}", arr[]); System.Console.ReadLine();
}
}
/* Output:
在Main方法中,调用Change方法之前,pArray中第一个元素的值是: 1
在Change方法中,pArray中第一个元素的值是: -3
在Main方法中,调用Change方法后,pArray中第一个元素的值是: -3
*/
方法内发生的所有更改都影响 Main 中的原始数组。实际上,使用 new 运算符对原始数组对象进行了重新分配内存的操作。因此,调用 Change 方法后,对 arr 的任何引用都将指向 Change 方法中创建的新的数组。
交换字符串是传递引用类型参数的很好的示例。
1. 通过值传递:worker 和 manager 两个字符串在 Main 中进行初始化,并作为一般参数传递给静态方法 swap。这两个字符串在 swap 方法内进行了交,但在 Main 内没有进行交换。
class SwapEmployees
{
// 传值
static void swap(string e1, string e2)
{
string temp = e1;
e1 = e2;
e2 = temp;
System.Console.WriteLine("在swap方法里:{0} {1}", e1, e2);
} static void Main()
{
string worker = "John";
string manager = "Smith";
System.Console.WriteLine("在Main方法里,调用swap方法之前:{0} {1}", worker, manager); swap(worker, manager); // 通过引用传递方式,来传递字符串变量
System.Console.WriteLine("在Main方法里,调用swap方法之后:{0} {1}", worker, manager); System.Console.ReadLine();
}
} /* Output:
在Main方法里,调用swap方法之前:John Smith
在swap方法里:Smith John
在Main方法里,调用swap方法之后:John Smith
*/
通过值传递时,对应的内存变化如图一所示:

图一:通过值传递引用类型参数时的内存变化
2. 通过引用传递:worker 和 manager 两个字符串在 Main 中进行初始化,并作为参数(带 ref 关键字)传递给静态方法 swap。这两个字符串在该方法内和 Main 内均进行了交换。
class SwapEmployees
{
// 传引用
static void swap(ref string e1, ref string e2)
{
string temp = e1;
e1 = e2;
e2 = temp;
System.Console.WriteLine("在swap方法里:{0} {1}", e1, e2);
} static void Main()
{
string worker = "John";
string manager = "Smith";
System.Console.WriteLine("在Main方法里,调用swap方法之前:{0} {1}", worker, manager); swap(ref worker, ref manager); // 通过引用传递方式,来传递字符串变量
System.Console.WriteLine("在Main方法里,调用swap方法之后:{0} {1}", worker, manager); System.Console.ReadLine();
}
} /* Output:
在Main方法里,调用swap方法之前:John Smith
在swap方法里:Smith John
在Main方法里,调用swap方法之后:Smith John
*/
通过引用传递时,对应的内存变化如图二所示:

图二:通过引用传递引用类型参数时的内存变化
总结:
本示例中,需要通过传递引用的方式来传递参数,才能影响调用程序中的对象。如果把方法声明和方法调用中的 ref 关键字都去掉,那么调用方法后,原对象不会被重新分配内存。不难发现,上面的两种方式与值类型和引用类型的存储方式密切相关。关于值类型和引用类型的存储方式的区别,可以参考文章:http://www.xuexila.com/baikezhishi/536966.html
//摘录其中一段
与 C++ 不同,Java 自动管理栈和堆,程序员不能直接地设置栈或堆。
Java 的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过 new, newarray, anewarray, multianewarray 等指令建立,它们不需要程序代码来显式的释放。堆由垃圾回收来负责,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java 的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
栈的优势是,存取速度比堆要快,仅次于寄存器,栈的数据还可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理 int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理 int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了 a 与 b 同时均指向3的情况。这时,如果再令 a = 4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令 a 指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况 a 的修改并不会影响到 b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量 。
官方文档:
传递引用类型参数的两种方式(转自 MSDN)的更多相关文章
- 【前台 ajax】web项目前台传递数组给后台 两种方式
项目使用maven springMVC 有需求 将前台的数组 在ajax中 送给后台 方式1: 前台代码:[注意:ajax中的属性---traditional:true, ] 如果Post ...
- Restful传递数组参数的两种方式
第一种,直接传递数组 js直接传递数组 var data = ["123","456"];that.loadDictionarys(data).subscrib ...
- Json传递数据两种方式(json大全)
1.Json传递数据两种方式(json大全)----------------------------字符串 var list1 = ["number","name&quo ...
- Eclipse项目中引用第三方jar包时将项目打包成jar文件的两种方式
转载自:http://www.cnblogs.com/lanxuezaipiao/p/3291641.html 方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFES ...
- JS实现把一个页面层数据传递到另一个页面的两种方式
本博客整理了两种方式从一个页面层向另一个页面层传递参数. 一. 通过cookie方式 1. 传递cookie页面的html,此处命名为a.html 请输入用户名和密码: <input id=&q ...
- 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入
在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...
- 使用Fragment的两种方式:<fragment>与<FrameLayout>
Android中使用Fragment的两种方式:<fragment>与<FrameLayout> 1.静态使用:自定义类,继承Fragment,在xml中使用<fragm ...
- jquery ajax提交表单数据的两种方式
http://www.kwstu.com/ArticleView/kwstu_201331316441313 貌似AJAX越来越火了,作为一个WEB程序开发者要是不会这个感觉就要落伍,甚至有可能在求职 ...
- SqlServer2008 数据库同步的两种方式(Sql JOB)
尊重原著作:本文转载自http://www.cnblogs.com/tyb1222/archive/2011/05/27/2060075.html 数据库同步是一种比较常用的功能.下面介绍的就是数据库 ...
随机推荐
- mysql exists 和 in的效率比较
这条语句适用于a表比b表大的情况 select * from ecs_goods a where cat_id in(select cat_id from ecs_category b); 这条语句适 ...
- 五大权限:UGO权限、SetUID SetGID Sticky、ACL权限、chattr(文件系统级别的权限)、SELINUX
五大权限:UGO权限.SetUID SetGID Sticky.ACL权限.chattr(文件系统级别的权限).SELINUX ======================文件属性以及ugo权限= ...
- Deep Learning 15:RBM的学习
RBM是深度学习的核心,所以必须彻底清楚地理解RBM原理.推导及其训练方法 1.读学位论文“基于深度学习的人脸识别研究”: 对RBM.DBN的介绍比较详细,可以作为基础阅读,再去读英文论文. 2.RB ...
- D3.js 做一个简单的图表(条形图)
柱形图是一种最简单的可视化图标,主要有矩形.文字标签.坐标轴组成. 本文为简单起见,只绘制矩形的部分,用以讲解如何使用 D3 在 SVG 画布中绘图. 一. 画布是什么 前几章的处理对象都是 HTML ...
- Server asks us to fall back to SIMPLE auth, but this client is configured to only allow secure connections.
我是在flume向hdfs 写(sink)数据时遇到的这个错误. Server (是指hdfs) asks us to fall back to SIMPLE auth, but this clien ...
- spring profile 多环境配置管理
本地.测试.开发.产品等不同环境文件配置 现象 如果在开发时进行一些数据库测试,希望链接到一个测试的数据库,以避免对开发数据库的影响. 开发时的某些配置比如log4j日志的级别,和生产环境又有 ...
- 树(一)——线段树
问题 现在有1~30这30个数,数N被抽上的概率正比于1/sqrt(N+1),求满足这个概率分布的随机数发生器. 思路 第一,如何解决这个"概率正比"问题. 第二,如何产生满足条件 ...
- Objective-C( block的使用)
block block用来保存一段代码 block的标志:^ block跟函数很像:可以保存代码.有返回值.有形参.调用方式跟调用方法一样 block内部可以访问外面的变量 默认情况下,block内部 ...
- [转]Java 8:不要再用循环了
以下内容为转载,没有在jdk8中测试,具体业务场景是否存在BUG或使用需要注意的地方有待测试. ------------------分割线---------------------- 正如我之前所写的 ...
- WCF初探-4:WCF消息交换模式之请求与答复模式
请求与答复模式( Request/Reply) 这种交换模式是使用最多的一中,它有如下特征: 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型 相比Duplex来讲,这种模式强调的 ...