Java:为什么需要包装类?
在非常高的层次上,我知道我们需要通过使用它们各自的包装类来“包装”原始数据类型,例如int和char,以便在Java集合中使用它们。我想了解Java集合在Java集合中是如何工作的。低级别问:“为什么我们需要将原始数据类型包装为能够在集合中使用它们的对象?”我提前感谢您的帮助。
在虚拟机级别,这是因为与java.lang.Object及其派生类型等引用类型相比,原始类型在内存中的表示方式非常不同。 例如,Java中的原始int在内存中只有4个字节,而Object本身至少占用8个字节,另外还有4个字节用于引用它。 这种设计简单地反映了CPU可以更有效地处理原始类型的事实。
因此,对于“为什么需要包装类型”这一问题的一个答案是因为它能够提高性能。
但是对于程序员来说,这种区别会增加一些不良的认知开销(例如,不能在集合中使用int和float。)实际上,通过隐藏这种区别很可能进行语言设计 – 许多脚本语言都是这样做的,并且CLR就是这么做的。 从1.5开始,Java也是这样做的。 这是通过让编译器在基本表示和对象表示(通常称为装箱/拆箱)之间静默插入必要的转换来实现的。
所以对你的问题的另一个答案是,“不,我们不需要它”,因为编译器会自动为你做这件事,并且在某种程度上你可以忘记幕后发生的事情。
因为Java集合只能存储对象引用(因此您需要将原始图形存储在集合中)。
阅读这篇关于Autoboxing的简短文章了解更多信息。
如果你想要细节,那么它几乎归结为以下几点:
Local Primitives存储在Stack上。 集合通过引用堆中对象的内存位置来存储它们的值。 要获取本地原语的引用,您必须使用框(在堆栈上获取值并将其包装以存储在堆上)的值。
要将原始类型值存储在集合类中,我们需要Wrapper classe。
原始数据类型不能作为内存地址引用。 这就是为什么我们需要包装器作为原始值的占位符。 然后可以对这些值进行变异和访问,重组,排序或随机化。
看拳击和拆箱:什么时候出现?
它适用于C#,但同样的概念适用于Java。 John Skeet写了答案。
嗯,原因是因为Java集合没有区分原语和Object。 它将它们全部作为Object处理,因此需要一个包装器。 您可以轻松地构建自己不需要包装器的集合类,但最后,您必须为每个类型char,int,float,double等构建一个,以及集合的类型(Set,Map,列表,+他们的实现)。
你能想象这有多无聊吗?
事实上,它通过不使用包装器带来的性能几乎可以忽略不计。 然而,如果您需要非常高的性能,也可以使用一些原始集合库(例如http://www.joda.org/joda-primitives/ )
Collection使用Generics作为基础。 Collection Framework旨在收集,存储和操作任何类的数据。 所以它使用generics类型。 通过使用generics,它能够存储您在其声明中指定的任何类的数据。
现在我们有各种场景,希望以与集合相同的方式存储基元数据。 我们无法使用ArrayList,HashSet等Collection类存储原始数据,因为Collection类只能存储对象。 因此,为了在Collection中存储原始类型,我们提供了包装类。
包装类提供了与相应数据类型相关的有用方法,您可以在某些情况下使用这些方法。
一个简单的例子。 想想这个,
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!
你明白了吗?
如果已知变量要么保持表示null
的特定位模式,要么保存可用于定位Java虚拟机对象标头的信息,并且如果给定位模式,用于读取给定引用的对象标头的方法将固有地陷阱与null
相关联,然后JVM可以在假设存在变量的情况下访问由变量标识的对象。 如果变量可能包含某些不是有效引用但不是特定null
模式的内容,那么任何试图使用该变量的代码都必须首先检查它是否标识了一个对象。 这将大大减慢JVM的速度。
如果Object
派生自Anything
,而派生自Object
类对象,但是从Anything
派生的不同类inheritance的原语,则在64位实现中,可能实际上可以说大约3/4的可能位模式将代表double
低于2 ^ 512的值,其中1/8表示+/- 1,152,921,504,606,846,975范围内的long
值,几十亿表示任何其他原始值的任何可能值,而1/256表示对象。 Anything
类型的Anything
上的许多种操作都比Object
类型慢,但这种操作不会非常频繁; 在尝试使用之前,大多数代码最终会将某些Anything
为更具体的类型; 存在于Anything
的实际类型需要在演员表之前进行检查,但不能在演员表演之后进行。 但是,如果持有对堆类型的引用的变量与持有“任何东西”的变量之间没有区别,则无法避免开销比其他方式或应该进一步延长得多。
与String类非常相似,Wrappers提供了附加function,使程序员能够在数据存储过程中做更多工作。 因此人们使用String类就像….
String uglyString = "fUbAr"; String myStr = uglyString.toLower();
他们也可以和Wrapper一起使用。 类似的想法。
这是除了Bharat上面提到的集合/generics的打字问题之外。
阅读所有答案,但没有一个真正以外行的方式解释它。
包装器类围绕数据类型(可以是任何基本数据类型,如int,char,byte,long)包装(包围),并使其成为对象 。
以下是需要包装类的几个原因:
- 允许
null
值。 - 可用于
List
,Map
等集合中。 - 可以在接受
Object
类型参数的方法中使用。 -
可以使用
new ClassName()
像其他对象一样创建对象:Integer wrapperInt = new Integer("10");
- 使
Object
类具有的所有函数可用,例如clone()
,equals()
,hashCode()
,toString()
等。
可以通过两种方式创建包装类:
-
使用构造函数:
Integer i = new Integer("1"); //new object is created
-
使用
valueOf()
静态运算符:Integer i = Integer.valueOf("100"); //100 is stored in variable
建议使用第二种创建包装类的方法,因为它不会创建新对象所需的内存较少。
因为int不属于任何类。 我们将datatype(int)转换为object(Interger)