Java:为什么需要包装类?

时间:2010-01-25 19:03:54

标签: java collections

在非常高的层面上,我知道我们需要“包装”原始数据类型,例如int和char,通过使用它们各自的包装类在Java集合中使用它们。我想了解Java集合是如何通过询问:“为什么我们需要将原始数据类型包装为能够在集合中使用它们的对象来处理低级别的工作?”我提前感谢您的帮助。

12 个答案:

答案 0 :(得分:34)

因为Java集合只能存储对象引用(因此您需要将基元存储在集合中)。

阅读Autoboxing上的这篇简短文章,了解更多信息。

如果你想要细节,那么它几乎归结为以下几点:

Local Primitives存储在堆栈中。集合通过引用堆中对象的内存位置来存储它们的值。要获取本地原语的引用,您必须将值(在堆栈上取值并将其包装以存储在堆上)的值。

答案 1 :(得分:19)

在虚拟机级别,这是因为与java.lang.Object及其派生类型等引用类型相比,基本类型在内存中的表示方式非常不同。例如,Java中的原始int在内存中只有4个字节,而Object本身至少占用8个字节,另外还有4个字节用于引用它。这种设计简单地反映了CPU可以更有效地处理原始类型的事实。

因此,对于“为什么需要包装类型”这一问题的一个答案是因为它能够提高性能。

但是对于程序员来说,这种区别会增加一些不良的认知开销(例如,不能在集合中使用int和float。)事实上,通过隐藏这种区别很有可能进行语言设计 - 许多脚本语言都可以这个,CLR就是这么做的。从1.5开始,Java也是这样做的。这是通过让编译器在基本表示和对象表示(通常称为装箱/拆箱)之间静默插入必要的转换来实现的。

所以对你的问题的另一个答案是,“不,我们不需要它”,因为编译器会自动为你做这件事,并且在某种程度上你可以忘记幕后发生的事情。

答案 2 :(得分:4)

要将基本类型值存储在集合类中,我们需要Wrapper classe。

答案 3 :(得分:3)

原始数据类型不能作为内存地址引用。这就是为什么我们需要包装器作为原始值的占位符。然后可以对这些值进行变异和访问,重组,排序或随机化。

答案 4 :(得分:2)

阅读所有答案,但没有一个能够以外行的方式解释它。

包装器类围绕数据类型(可以是任何基本数据类型,如int,char,byte,long)包装(包围),并使其成为对象

以下是需要包装类的几个原因:

  1. 允许null值。
  2. 可用于ListMap
  3. 等集合
  4. 可用于接受Object类型参数的方法。
  5. 可以使用new ClassName()像其他对象一样创建对象:

    Integer wrapperInt = new Integer("10");
    
  6. 提供Object类所具有的所有功能,例如clone()equals()hashCode()toString()等。
  7. 可以通过两种方式创建包装类:

    1. 使用构造函数:

      Integer i = new Integer("1"); //new object is created
      
    2. 使用valueOf()静态运算符:

       Integer i  = Integer.valueOf("100"); //100 is stored in variable
      
    3. 建议使用第二种创建包装类的方法,因为它不需要创建新对象所需的内存。

答案 5 :(得分:1)

请参阅Boxing and unboxing: when does it come up?

适用于C#,但同样的概念适用于Java。 John Skeet写了答案。

答案 6 :(得分:1)

嗯,原因是Java集合没有区分原语和Object。它将它们全部作为Object处理,因此它需要一个包装器。您可以轻松地构建自己不需要包装器的集合类,但最后,您必须为每个类型char,int,float,double等构建一个,以及集合的类型(Set,Map,列表,+他们的实施)。

你能想象这有多无聊吗?

事实上,对于大多数应用程序而言,使用无包装器所带来的性能几乎可以忽略不计。然而,如果您需要非常高的性能,也可以使用一些原始集合库(例如http://www.joda.org/joda-primitives/

答案 7 :(得分:1)

Collection使用泛型作为基础。 Collection Framework旨在收集,存储和操作任何类的数据。所以它使用泛型类型。通过使用泛型,它能够存储您在其声明中指定的任何类的数据。

现在我们有各种场景,希望以与集合相同的方式存储基元数据。我们无法使用ArrayList,HashSet等Collection类存储原始数据,因为Collection类只能存储对象。因此,为了在Collection中存储原始类型,我们提供了包装类。

答案 8 :(得分:0)

包装类提供了与相应数据类型相关的有用方法,您可以在某些情况下使用这些方法。

一个简单的例子。考虑一下,

Integer x=new Integer(10); 
//to get the byte value of 10
x.byteValue(); 

//but you can't do this,
int x=10;
x.byteValue(); //Wrong!
你能明白这一点吗?

答案 9 :(得分:0)

如果已知变量要么保持表示null的特定位模式,要么可以使用可用于定位Java虚拟机对象标头的信息,以及用于读取给定引用的对象标头的方法将是如果给定与null相关联的位模式,则固有陷阱,然后JVM可以在假设存在变量的情况下访问变量所标识的对象。如果变量可以包含某些不是有效引用但不是特定null位模式的内容,那么任何试图使用该变量的代码都必须首先检查它是否标识了一个对象。这将大大减慢JVM的速度。

如果Object派生自Anything,类对象派生自Object,但原语继承自派生自Anything的不同类,则在64位实现中可能实际上可以说,大约3/4的可能位模式表示double值低于2 ^ 512,1 / 8表示{+ 1}}值,范围为+/- 1,152,921,504,606,846,975,几十亿代表任何其他原始的任何可能的价值,而1/256代表物品。对long类型的事物的多种操作比类型Anything慢,但这种操作不会非常频繁;在尝试使用之前,大多数代码最终会将Object转换为更具体的类型;存储在Anything中的实际类型需要在演员表之前进行检查,但不需要在执行演员表之后进行检查。但是,如果一个持有对堆类型的引用的变量与一个持有"任何东西"之间没有区别,那么就没有办法避免开销比它本来应该或应该的那样进一步延伸。

答案 10 :(得分:0)

与String类非常相似,Wrappers提供了附加功能,使程序员能够在数据存储过程中做更多工作。因此,人们使用String类就像....

String uglyString = "fUbAr"; String myStr = uglyString.toLower();

同样,他们也可以使用Wrapper。类似的想法。

这是Bharat上面提到的集合/泛型的输入问题。

答案 11 :(得分:0)

因为int不属于任何类。  我们将datatype(int)转换为object(Interger)