分享

as3:值类型和引用类型的区别

 昵称10426321 2012-07-16

在实际运用时,初学者所犯的错误中,有相当大的一部分来自于对值类型和引用类型的混淆。即使是编程老手,偶尔也会在这个地方马失前蹄。相比把数据类型分为基元数据类型和复杂数据类型,笔者认为,使用值类型和引用类型的分类,更加直观。在C#中,数据类型就分为值类型和引用类型。
值类型直接存储值;而引用类型存储引用,指向要操作的对象。C#中的值类型
正好对应于ActionScript3 中的基元数据类型,C#中的引用类型正好对应于
Action Script3
中的复杂数据类型。
那么在ActionScript3中怎样区分数据到底是值类型和引用类型呢?
ActionScript3
中基本类型只有BooleanintNumberString uint。那
么,很简单,ActionScript3 中值类型只有这几种,其余的数据类型就全是引用
类型。
除此之外,还有一个典型的特征:值类型的数据不用new 关键字来创建;
必须使用new关键字创建的一定不是值类型。
最关键的问题来了:值类型和引用类型在使用上有什么区别呢?
以值类型int和引用类型Array来做说明。先看值类型int的例子代码:
//
值类型的例子
var a:int = 3; //
声明变量a,赋值为3
var b:int = a; //
声明变量b,并将a 的值赋值给b
b = 9; //
改变变量b的值为9
trace ("a
现在的值是:"+a);
trace ("b
现在的值是:"+b);
//
输出结果:
//a
现在的值是:3
//b
现在的值是:9
根据结果可以发现,把a的赋值给b后,改变了b的值,a的值并没有因为
b
值的改变而变化。这就是值类型的特点:直接存值。每个变量的值不因为其他
变量值的改变而改变。
再看引用类型的情况,以Array为例:

//
引用类型的例子
var a:Array = new Array(1,2,3); //
声明变量a,新建一个数组[1,2,3]赋值给它
var b:Array = a; //
声明变量b,把变量a引用赋值给b
b[0] = 4; //
改变b数组第一个元素的值为4
trace ("a
现在的值是:"+a);
trace ("b
现在的值是:"+b);
//
输出结果:
//a
现在的值是:423
//b
现在的值是:423
同样是把a 的值赋给b 的值,但b 的值改变后,a 的值也跟着改变了。很多
初学者都不会想到改变b 值会影响到a 的值,因此大量的此种类型的程序臭虫
Bug)就会出现。
那么,最后一个问题:为什么两种类型的行为有这么的区别?
原因就在于,引用类型数据存储的是引用。引用,也就是我们之前所说的遥
控器,它指向一个对象。对象都是通过引用来操纵的。当我们操纵一个引用数据
类型时,比如说上例中的Array数据类型,其实并没有直接操作数据,而是和这
个遥控器在打交道。当“var b:Array=a;”这一句执行时,实际上是新创建了一
个数组变量b,将a 持有的引用(而不是值)赋值给了b。因此遥控器a和遥控
b 同时指向了一个数组对象,那么任何一个变量做了什么操作,另外一个变量
看起来也就受到了影响。
下面我们来逐句解释在这些代码后真实发生的事儿。
在本例中,第一行代码告诉Flash Player在内存中创建一个数组[1,2,3]。然
后设置变量a持有的遥控器指向这个数组,用正式的话说就是,把这个数组的引
用赋值给变量a。第二行代码中,当我们把a 的值赋给b 时,其实Flash Player
并没有在内存中再创建一个新的数组[1,2,3],而是直接把变量a 的引用又给了b
因此,b 的引用和a 的引用完全一样,都是指向原来的数组[1,2,3]。所以,当我
们通过b 变量改变数组值时,就改变了原来的那个唯一的数组。由于b a
控器控制的都是同一个对象,所以,理所当然,a 的输出结果也变了。

好,理解后,我们再看这种情况:
var a:Array = new Array(1,2,3);
var b:Array = a;
b = new Array(4,5,6); //
新建一个数组[4,5,6],将引用赋值给b
b [0] = 100;
trace ("a
现在的值是:" + a);
trace ("a
现在的值是:" + a);
//
输出结果:
//a
现在的值是:123
//b
现在的值是:10056
我们看到ab 的值居然互不干扰了。原因是什么呢?请看第三句“b=new
Array(4,5,6)”
,原来用new关键字让Flash Player有创建了一个新的数组[45
6]
,并让b 这个遥控器指向了它。所以ab 的引用指向不再一样了,分别指向
内存中两个不同的数组。因此,改变b 的内容并不会影响a 的内容,双方终于
互不干涉内政了。
上面说过,ActionScript3中变量持有引用,指向要操作的对象。和Java
同,ActionScript3 变量本身是不能持有值的。在ActionScript3 中,值类型变量
持有的是指向值类型数据的引用;引用类型变量持有的是指向引用类型数据的引
用。不要忘记,不论值类型数据还是引用类型数据,其实质都是对象。值类型即
前面说过的基元数据类型:引用类型即前面说过的复杂数据类型。所不同的是,
值类型数据是一种不变对象,解释详见下面这一节。
2.2.5
基元数据类型的深入讨论*
在上一节中提到,值类型数据和引用类型数据的操作有着那么大的不同。但
是,这两种数据类型的变量持有的不都是引用吗?既然是引用,就都是指向对象
的,那么为什么会有这么大的差别呢?
Java 中,值类型并不是以对象形式存在的。值类型的变量,存储的不是引用,而是直接容纳了具体的值(Value)。在ActionScript3中则不一样,因为本质上,值类型仍然是对象。

那么,即使是值类型变量,存储的仍然是引用,而不是直接持有值。但是,
值类型是一种特殊的对象,叫做不变对象(immutable object)。正是这种对象
的特殊行为导致了我们对值类型的使用方式和引用类型并不相同。
不变对象,顾名思义,一旦被建立后,就不能再被更改。有些操作看起来似
乎是要更改了不变对象中的内容,但实际上不是。一旦虚拟机发现指向一个对象
的引用要改变该不变对象的值,就会另行创建一个新的不变对象来接受新的值。
比如说下面这个例子:
var aname:int = 1;
aname = aname + 2;
第一行创立了一个int 类型的不变对象A出来,它的值是1,并赋给了变量
aname
。第二行加上了一个整型值2,改变了aname 的值。但是,不变对象A
并没有改变。实际上发生的事儿是:第二行的结果是导致了一个新的整型值不变
对象B 被创建,B 的值为3。然后不变对象B 的引用被赋给变量aname。换句
话说,此时aname 持有的引用不载指向不变对象A,而是指向新创建的不变对
B。这就是不变对象工作原理的一个示范。不变对象A怎么办?由于它不再被
使用,会被AVMActionScript虚拟机)自动回收。
基本类型intuintbooleanNumberString都是不变对象。除String
现上稍有特殊外,其余的原理相同。
那么为什么要设计出这么一种不变对象,而不采用直接存值的方式呢?这有
多方面的考虑。一方面,将基元数据类型用不变对象的方法来实现,使得引用的
效率和传值一样高。另一方面,由于变量持有的是引用,而不是直接持有值,导
致不变对象可以被重复引用。而引用的内存消耗一般比值要小很多,所以对内存
的使用率也会大大提高。举个例子,如果有10000 个字符串变量,值都是
“abcde”
。那么实际上只有一个值为“abcde”的不变对象在内存中被创建。
10000
个字符串持有的都是对这个不变对象的引用而已。相比在内存中创建
10000
个同样的字符串,这10000 个引用的内存消耗是相当低的。我们来想象
一下,如果这个字符串值是一个很长的字符串呢?比如说长度为10000 个字母
的字符串。可想而知,这种设计对内存的节省会是多么巨大。

因此,由于这种不变对象的机制,基本数据的执行效率是相当高的。一般比
复杂类型快数倍。在Java 中,有人做过专门测试,得出的结论是要快10 倍左
右。在一些特色的对性能要求高的场合,基元数据类型的优先使用可以时效率大
大提高。

--节选自《flash as3殿堂之路》

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多