Java 为什么有些语言需要装箱和拆箱?
这不是什么是装箱和拆箱的问题, 这相当于为什么像Java和C#这样的语言需要这个呢 我非常熟悉C++、STL和Booost。 在C++中,我可以很容易地写出这样的东西,Java 为什么有些语言需要装箱和拆箱?,java,c++,generics,boxing,unboxing,Java,C++,Generics,Boxing,Unboxing,这不是什么是装箱和拆箱的问题, 这相当于为什么像Java和C#这样的语言需要这个呢 我非常熟悉C++、STL和Booost。 在C++中,我可以很容易地写出这样的东西, std::vector<double> dummy; std::向量虚拟; 我有一些Java方面的经验,但我真的很惊讶,因为我不得不写这样的东西 ArrayList<Double> dummy = new ArrayList<Double>(); ArrayList dummy=new
std::vector<double> dummy;
std::向量虚拟;
我有一些Java方面的经验,但我真的很惊讶,因为我不得不写这样的东西
ArrayList<Double> dummy = new ArrayList<Double>();
ArrayList dummy=new ArrayList();
我的问题是,为什么它应该是一个对象,在谈到泛型时,从技术上讲,包含基元类型有什么困难?装箱和拆箱是语言(如C#和Java)实现其内存分配策略的必要条件 某些类型在堆栈上分配,其他类型在堆上分配。为了将堆栈分配的类型视为堆分配的类型,需要装箱以将堆栈分配的类型移动到堆上。拆箱是相反的过程 在C#中,堆栈分配的类型称为值类型(例如
System.Int32
和System.DateTime
),堆分配的类型称为引用类型(例如System.Stream
和System.String
)
在某些情况下,将值类型视为引用类型是有利的(反射就是一个例子),但在大多数情况下,最好避免装箱和取消装箱。我认为这也是因为原语不会从对象继承。假设您有一个方法希望能够接受任何东西作为参数,例如
class Printer {
public void print(Object o) {
...
}
}
您可能需要向该方法传递一个简单的基元值,如:
printer.print(5);
您可以在不装箱/取消装箱的情况下执行此操作,因为5是基本体,而不是对象。您可以为每个基元类型重载print方法以启用此类功能,但这是一个难题。在Java和C#(与C++不同)中,一切都扩展了对象,因此像ArrayList这样的集合类可以保存对象或其任何子体(基本上是任何东西)
然而,出于性能原因,java中的原语或C#中的值类型被赋予了特殊的状态。它们不是目标。您不能执行以下操作(在Java中):
即使toString是对象上的一个方法。为了将这一点与性能联系起来,创建了等效的对象。自动装箱删除了必须将原语放入其包装类并再次取出的样板代码,从而使代码更具可读性
C#中的值类型和对象之间的差异更为灰色。看看他们有什么不同
在谈到泛型时,从技术上讲,包含基元类型有什么困难
就Java而言,这是因为泛型的工作方式。在Java中,泛型是一种编译时技巧,它阻止您将图像
对象放入数组列表
。但是,Java的泛型是通过类型擦除实现的:泛型类型信息在运行时丢失。这是出于兼容性的原因,因为泛型是在Java生命的后期添加的。这意味着,在运行时,ArrayList
实际上是一个ArrayList
(或者更好:只是ArrayList
,它在所有方法中期望并返回Object
),在检索值时自动转换为String
但是由于int
不是从Object
派生的,因此不能将其放入(运行时)期望Object
的数组列表中,也不能将Object
强制转换为int
。这意味着必须将原语int
包装为从对象
继承的类型,如整数
例如,C#的工作原理不同。C#中的泛型也在运行时强制执行,并且不需要对
列表进行装箱。C#中的装箱仅在尝试将值类型(如int
)存储在引用类型变量(如object
)中时发生。由于C#中的int
继承自C#中的Object
,因此写入Object obj=2
是完全有效的,但是int将被装箱,这是由编译器自动完成的(没有Integer
引用类型向用户公开或任何内容).我只能告诉您为什么Java在泛型中不支持primitve类型
首先是一个问题,每次支持这个问题都会引起关于java是否应该有基元类型的讨论。这当然阻碍了对实际问题的讨论
第二个不包括它的主要原因是他们想要二进制向后兼容性,这样它就可以在不知道泛型的VM上不加修改地运行。这种向后兼容性/迁移兼容性也是为什么现在Collections API支持泛型并保持不变,而没有一套全新的泛型感知集合API(正如C#引入泛型时一样)
兼容性是使用ersure(在编译时删除的泛型类型参数信息)实现的,这也是在java中获得如此多未检查强制转换警告的原因
您仍然可以添加具体化的泛型,但这并不容易。仅仅添加类型信息添加运行时而不是删除它将不起作用,因为它破坏了源代码和二进制代码的兼容性(您不能继续使用原始类型,也不能调用现有的编译代码,因为它们没有相应的方法)
另一种方法是C#选择的方法:见上文
这个用例不支持自动装箱/取消装箱,因为自动装箱成本太高
堆上存储的每个非数组非字符串对象都包含一个8或16字节的头(大小适用于32/64位系统),后跟该对象的公共和私有字段的内容。数组和字符串有上面的头,再加上一些字节,定义数组的长度和每个元素的大小(可能是
7.toString()