Java:为什么需要包装类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2134798/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 19:37:34  来源:igfitidea点击:

Java: Why are wrapper classes needed?

javacollections

提问by Midnight Blue

On the very high level, I know that we need to "wrap" the primitive data types, such as int and char, by using their respective wrapper classes to use them within Java collections.I would like to understand how Java collections work at the low level by asking:"why do we need to wrap primitive data types as objects to be able to use them in collections?"I thank you in advance for your help.

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

采纳答案by Kohsuke Kawaguchi

At the virtual machine level, it's because primitive types are represented very differently in memory compared to reference types like java.lang.Object and its derived types. Primitive int in Java for example is just 4 bytes in memory, whereas an Object takes up at minimum 8 bytes by itself, plus another 4 bytes for referencing it. Such design is a simple reflection of the fact that CPUs can treat primitive types much more efficiently.

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

So one answer to your question "why wrapper types are needed" is because of performance improvement that it enables.

因此,您的问题“为什么需要包装器类型”的一个答案是因为它实现了性能改进。

But for programmers, such distinction adds some undesirable cognitive overhead (e.g., can't use int and float in collections.) In fact, it's quite possible to do a language design by hiding that distinction --- many scripting languages do this, and CLR does that. Starting 1.5, Java does that, too. This is achieved by letting the compiler silently insert necessary conversion between primitive representation and Object representation (which is commonly referred to as boxing/unboxing.)

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

So another answer to your question is, "no, we don't need it", because the compiler does that automatically for you, and to certain extent you can forget what's going on behind the scene.

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

回答by Justin Niessner

Because Java collections can only store Object References (so you need to box primitives to store them in collections).

因为 Java 集合只能存储对象引用(因此您需要将原语装箱以将它们存储在集合中)。

Read this short article on Autoboxingfor more info.

阅读这篇关于自动装箱的短文了解更多信息。

If you want the nitty gritty details, it pretty much boils down to the following:

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

Local Primitives are stored on the Stack. Collections store their values via a reference to an Object's memory location in the Heap. To get that reference for a local primitive, you have to box (take the value on the Stack and wrap it for storage on the Heap) the value.

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

回答by Stardust

To store the primitive type values in collection classes we require Wrapper classe.

要将原始类型值存储在集合类中,我们需要包装类。

回答by Pritam Banerjee

Read all of the answers, but none of them really explains it simply in layman terms.

阅读所有答案,但没有一个真正用外行的术语解释它。

A wrapperclass wraps(encloses) around a data type (can be any primitive data type such as int, char, byte, long) and makes it an object.

包装类包装(封装)围绕一个数据类型(可以是任何原始数据类型如int,焦炭,字节长),并使其成为一个对象

Here are a few reasons why wrapper classes are needed:

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

  1. Allows nullvalues.
  2. Can be used in collection such as List, Map, etc.
  3. Can be used in methods which accepts arguments of Objecttype.
  4. Can be created like Objects using new ClassName()like other objects:

    Integer wrapperInt = new Integer("10");
    
  5. Makes available all the functions that Objectclass has such as clone(), equals(), hashCode(), toString()etc.
  1. 允许null值。
  2. 可以收集诸如使用ListMap等等。
  3. 可以在接受Object类型参数的方法中使用。
  4. 可以像使用new ClassName()其他对象一样创建对象:

    Integer wrapperInt = new Integer("10");
    
  5. 使得所有可用的功能Object类有如clone()equals()hashCode()toString()等。

Wrapper classes can be created in two ways:

可以通过两种方式创建包装类:

  1. Using constructor:

    Integer i = new Integer("1"); //new object is created
    
  2. Using valueOf()static operators:

     Integer i  = Integer.valueOf("100"); //100 is stored in variable
    
  1. 使用构造函数:

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

     Integer i  = Integer.valueOf("100"); //100 is stored in variable
    

It is advised to use the second way of creating wrapper classes as it takes less memory as a new object is not created.

建议使用第二种创建包装类的方法,因为它占用的内存较少,因为没有创建新对象。

回答by Boris Pavlovi?

Primitive data types can't be referenced as memory addresses. That's why we need wrappers which serve as placeholders for primitive values. These values then can be mutated and accessed, reorganized, sorted or randomized.

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

回答by Bharat

Collection uses Generics as the bases. The Collection Framework is designed to collect, store and manipulate the data of any class. So it uses generic type. By using Generics it is capable of storing the data of ANY CLASS whose name you specify in its declaration.

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

Now we have various scenario in which want to store the primitive data in the same manner in which the collection works. We have no way to store primitive data using Collection classes like ArrayList, HashSet etc because Collection classes can store objects only. So for storing primitive types in Collection we are provided with wrapper classes.

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

回答by ewernli

See Boxing and unboxing: when does it come up?

参见装箱和拆箱:什么时候出现?

It's for C#, but the same concept apply to Java. And John Skeet wrote the answer.

它适用于 C#,但同样的概念也适用于 Java。约翰斯基特写下了答案。

回答by nanda

Well, the reason is because Java collections doesn't differentiate between primitive and Object. It processes them all as Object and therefore, it will need a wrapper. You can easily build your own collection class that doesn't need wrapper, but at the end, you will have to build one for each type char, int, float, double, etc multiply by the types of the collections (Set, Map, List, + their implementation).

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

Can you imagine how boring that is?

你能想象那有多无聊吗?

And the fact is, the performance it brings by using no wrapper is almost negligible for most applications. Yet if you need very high performance, some libraries for primitive collections are also available (e.g. http://www.joda.org/joda-primitives/)

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

回答by everlasto

Wrapper classes provide useful methods related to corresponding data types which you can make use of in certain cases.

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

One simple example. Consider this,

一个简单的例子。考虑到这一点,

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!

can you get the point?

你能明白吗?

回答by supercat

If a variable is known to either hold a specific bit pattern representing nullor else information which can be used to locate a Java Virtual Machine object header, and if the method for reading an object header given a reference will inherently trap if given the bit pattern associated with null, then the JVM can access the object identified by the variable on the assumption that there is one. If a variable could hold something which wasn't a valid reference but wasn't the specific nullbit pattern, any code which tried to use that variable would have to first check whether it identified an object. That would greatly slow down the JVM.

如果已知变量包含表示特定位模式的特定位模式null或可用于定位 Java 虚拟机对象头的信息,并且如果给定引用的读取对象头的方法将固有地捕获,如果给定相关的位模式用null,则JVM可以在假设有一个访问由变量标识的对象。如果一个变量可以保存一些不是有效引用但不是特定null位模式的东西,那么任何尝试使用该变量的代码都必须首先检查它是否标识了一个对象。这会大大降低 JVM 的速度。

If Objectderived from Anything, and class objects derived from Object, but primitives inherited from a different class derived from Anything, then in a 64-bit implementation it might be practical to say that about 3/4 of the possible bit patterns would represent doublevalues below 2^512, 1/8 of them to represent longvalues in the range +/- 1,152,921,504,606,846,975, a few billion to represent any possible value of any other primitve, and the 1/256 to identify objects. Many kinds of operations on things of type Anythingwould be slower than with type Object, but such operations would not be terribly frequent; most code would end up casting Anythingto some more specific type before trying to work with it; the actual type stored in the Anythingwould need to be checked before the cast, but not after the cast was performed. Absent a distinction between a variable holding a reference to a heap type, however, versus one holding "anything", there would be no way to avoid having the overhead extend considerably further than it otherwise would or should.

如果Object派生自Anything和派生自 的类对象Object,但基元继承自派生自 的不同类Anything,那么在 64 位实现中,可能说大约 3/4 的可能位模式将表示double低于 2^512 的值可能是切合实际的, 其中 1/8 表示long+/- 1,152,921,504,606,846,975 范围内的值,数十亿表示任何其他原始值的任何可能值,1/ 256 表示对象。对 type 事物的多种操作Anything会比 type 慢Object,但这种操作不会非常频繁;大多数代码Anything在尝试使用它之前最终会转换为一些更具体的类型;实际类型存储在Anything需要在演员表之前检查,而不是在演员表执行后检查。但是,如果不区分持有对堆类型的引用的变量与持有“任何东西”的变量,则无法避免开销比原本应该或应该的扩展得更远。