您的位置:首页 > 编程语言 > Java开发

.NET中装箱拆箱与JAVA的异同

2010-12-02 23:07 369 查看
在.NET框架中,一种称作装箱(boxing)的机制用来将一个值类型转换为一个引用类型。装箱操作通常由以下几步组成:1.从托管堆中为新生成的引用类型对象分配内存。分配内存大小为,值类型实例本身的大小加上其他额外的将该值类型实例视为真正的引用对象所需的空间,这些额外的空间包括一个方法表指针和一个SyncBlockIndex。2.将值类型实例的字段拷贝到托管堆上新分配对象的内存中。3.返回托管堆中新分配对象的地址。该地址就是一个指向对象的引用。值类型实例也就变成了一个引用类型对象。

这里需要说明,某些编译器(C#)会根据需要自动产生对值类型实例进行装箱的IL代码。原先的值类型实例在装箱后可以被重用或释放,新的引用对它一无所知。经过装箱的值类型实例的生存期超出了未装箱的值类型实例的生存期。而拆箱过程则是CLR首先获取已装箱的对象中属于值类型的那部分字段的地址,然后将这些字段的值从托管堆拷贝到位于线程堆栈上的值类型实例中(此步不属于拆箱)。一般拆箱操作的代价要比装箱操作小得多。拆箱操作仅仅是获取指向对象中包含的值类型部分(数据字段)的指针而已,它不会像装箱操作那样涉及到任何内存字节的拷贝。

对一个引用类型的拆箱操作通常由以下几步组成:1.如果该引用为null,将会抛出一个NullReferenceException异常。2.如果该引用指向的对象不是一个期望的值类型的已装箱对象,将会抛出一个InvalidCastException异常。3.一个指向包含在已装箱对象中值类型部分的指针被返回。该指针指向的值类型对于引用类型对象通常所具有的附加成员(即一个方法表指针和一个SyncBlockIndex)一无所知。实际上,该指针指向的是已装箱对象中的未装箱部分。

下面我们来看一个例子。

public static void Main(){

Int32 v = 5; //创建一个未装箱的值类型变量

Object o = v; // o指向一个已装箱的,值为5的Int32

v = 123; //将未装箱值改为123

Console.WriteLine(v + ", " + (Int32) o); //显示123,5

}

上面一共有3次装箱和1次拆箱操作:第一次对v进行装箱,并将得到的指针存放在o中;第二次对v执行装箱,并将得到的指针存放在堆栈上等待Concat操作;第三次对o执行拆箱,将指向已装箱对象内部的Int32字段的指针和字节置于堆栈上;第四次对Int32值进行装箱操作,并将得到的指针放在堆栈上等待Concat操作。

如果我们知道自己编写的代码会导致编译器反复地对一个值类型进行装箱,那么我们应该自己来做这样的装箱操作,因为这将能够减少生成的代码量,并提高代码运行速度。

下面我们再来看看java的装箱拆箱操作。java的包装器包括使用基本包装器类或自动装箱及拆箱。java API的包装器类有两个主要用途:1.为把基本值“包装”在对象内提供一种机制,这使基本值能够包含在为对象保留的操作之中,如添加到Collections中,或者从带对象返回值的方法中返回。2.为基本类型提供分类功能。这些功能中的大多数与各种转换有关:在基本类型与String对象之间来回转换,在基本类型和String对象在不同基数(二进制,八进制和十六进制)之间转换。使用装箱的一般规则是:无论何时可以正常使用一个基本变量或一个包装对象,装箱和取消装箱都适用。如:

class UseBoxing{

public static void main(String[] args){

UseBoxing u = new UseBoxing();

u.go(5);

}

}

boolean go(Integer i){ //boxes the int it was passed

Boolean isSo = true;//boxes the literal

Short s = 300; //boxes th primitive

if(isSo){ //unboxing

System.out.println(++s);//unboxes,increments,reboxed

}

return !isSo;//unboxes,returns the inverse

}

over.

参考:《.NET框架程序设计》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: