为什么我们在Java中使用自动装箱和拆箱?


80

自动装箱是Java编译器在原始类型及其对应的对象包装器类之间进行的自动转换。例如,将int转换为Integer,将double转换为Double,依此类推。如果转换过程相反,这称为拆箱。

那么,为什么我们需要它?为什么要在Java中使用自动装箱和拆箱呢?


1
基本上是泛型..
nachokk 2014年

3
IntegerparseInt方法。int不具有。:)
Vishal Zanzrukia 2014年

@VishalZanzrukia那么,仅仅是为了获得更多功能?

11
你可以有List<Integer>,但你不能List<int>
Vishal Zanzrukia 2014年

是的..完全是..以获得更多功能
Vishal Zanzrukia 2014年

Answers:


171

需要一些上下文来充分理解其背后的主要原因。

基元与类

Java中的原始变量包含值(整数,双精度浮点二进制数等)。由于这些值的长度可能不同,因此包含它们的变量的长度也可能不同(请考虑floatdouble)。

另一方面,类变量包含对实例的引用。引用通常以多种语言实现为指针(或与指针非常相似的东西)。这些东西通常具有相同的大小,而不管它们指的是(实例的大小ObjectStringInteger,等)。

类变量的此属性使它们包含的引用可以互换(一定程度上)。这使我们能够执行所谓的替换:广义上讲,是将特定类型的实例用作另一种相关类型的实例(例如,将aString用作Object)。

原始变量不能以相同的方式互换彼此之间也不能互换Object。最明显的原因(但不是唯一原因)是它们的大小差异。这使得原始类型在这方面不方便,但是我们仍然需要语言中的它们(由于主要归结为性能的原因)。

泛型和类型擦除

泛型类型是具有一个或多个类型参数类型(确切的数字称为泛型arity)。例如,通用类型定义 List<T>具有类型参数T,可以是Object(产生具体类型 List<Object>),StringList<String>),IntegerList<Integer>)等。

泛型类型比非泛型类型复杂得多。当他们被介绍到Java(首次发行)之后,为了避免到JVM根本变化,并与旧的二进制可能破坏兼容性,Java的创作者决定在最小侵入性的方式来实现泛型类型:所有的具体类型List<T>实际上,已被编译为(的二进制等效项)List<Object>(对于其他类型,边界可能不是Object,但您明白了)。泛型和类型参数信息在此过程中丢失,这就是为什么我们将其称为类型擦除

将两者放在一起

现在的问题是上述现实的结合:如果在所有情况下都List<T>变为List<Object>,则T必须始终是可以直接分配给的类型Object。不允许其他任何事情。因为,正如我们之前所说,intfloat并且double不与互换Object,不能有一个List<int>List<float>List<double>(除非仿制药的显著更复杂的实现JVM中的存在)。

但是Java提供喜欢的类型IntegerFloat以及Double其在类实例包装这些原语,使他们有效地作为替代Object,从而使泛型类型与原语间接地工作,以及(因为你可以List<Integer>List<Float>List<Double>等)。

创建的过程Integer,从一个int,一个Floatfloat等,被称为拳击。相反的称为拆箱。因为每次使用原始语言都必须对原始语言进行装箱Object是很不方便的,所以在某些情况下该语言会自动执行此操作-这称为autoboxing


16

自动拳击用来以原始数据类型转换为自己的包装类对象。包装器类提供了对原始类型执行的多种功能。最常见的示例是:

int a = 56;
Integer i = a; // Auto Boxing

之所以需要它,是因为程序员可以轻松地直接编写代码,并且JVM将负责Boxing和Unboxing。

当我们使用java.util.Collection类型时,自动装箱也将派上用场。当我们要创建原始类型的Collection时,我们不能直接创建原始类型的Collection,我们只能创建Objects的Collection。例如 :

ArrayList<int> al = new ArrayList<int>(); // not supported 

ArrayList<Integer> al = new ArrayList<Integer>(); // supported 
al.add(45); //auto Boxing 

包装类

Java的8个原始类型(字节,短型,整数,浮点型,字符型,双精度型,布尔型,长型)中的每一个都具有单独的Wrapper类,与它们相关联。这些Wrapper类具有预定义的方法,用于对原始数据类型执行有用的操作。

包装器类的使用

String s = "45";
int a = Integer.parseInt(s); // sets the value of a to 45.

包装器类提供了许多有用的功能。在这里查看Java文档

取消装箱与自动装箱相反,在自动装箱中,我们将包装器类对象转换回其原始类型。这是由JVM自动完成的,因此我们可以对某些操作使用包装器类,然后将它们转换回原始类型,因为原始结果会加快处理速度。例如 :

Integer s = 45;
int a = s; auto UnBoxing;

对于使用对象的收藏集,仅使用自动拆箱。就是这样 :

ArrayList<Integer> al = new ArrayList<Integer>();
al.add(45);

int a = al.get(0); // returns the object of Integer . Automatically Unboxed . 

4

原始(非对象)类型在效率上有合理性。

基本类型int, boolean, double是立即数据,而Objects是引用。因此,字段(或变量)

int i;
double x;
Object s;

需要本地内存4 + 8 + 8?对于对象,仅存储对内存的引用(地址)。

使用对象包装器Integer, Double和其他包装器,将引入一个间接引用,该引用指向堆内存中的某些Integer / Double实例。

为什么需要拳击?

这是相对范围的问题。在将来的Java中,计划能够具有ArrayList<int>,提升基本类型。

答:目前,ArrayList仅适用于对象,为对象引用保留空间,并同样管理垃圾收集。因此,泛型类型是对象子级。因此,如果要使用浮点值的ArrayList,则需要将Double包裹在Double对象中。

这里的Java与传统的C ++模板不同:C ++类vector<string>, vector<int>将创建两个编译产品。Java设计是为了拥有一个ArrayList.class,而不是为每个参数类型都需要一个新的编译产品。

因此,在没有装箱到对象的情况下,将需要为每次出现的参数类型编译类。确切地说:每个集合或容器类都需要Object,int,double,boolean的版本。Object的版本将处理所有子类。

实际上,Java SE已经对IntBuffer,CharBuffer,DoubleBuffer等实现了这样的多样化需求,它们可以对int,char和double进行操作。通过从常见来源生成这些来源,以一种不可靠的方式解决了该问题。


4

从JDK 5开始,java添加了两个重要功能:自动装箱和自动拆箱。AutoBoxing是一个过程,只要需要这样的对象,就会自动将原始类型封装在等效的包装器中。您不必显式构造对象。自动拆箱是一个过程,通过该过程可以在需要封装对象的值时从其自动提取封装对象的值。您无需调用诸如intValue()doubleValue()之类的方法

自动装箱和自动拆箱的添加大大简化了编写算法,消除了手动装箱和装箱值的诱饵。避免错误也很有帮助。对于仅对对象进行操作的泛型来说,这也非常重要。最后,自动装箱有助于使用Collections Framework


2

我们为什么要装箱?

使我们在混合原语和其面向对象(OO)替代品的地方编写代码更加舒适/不太冗长。

为什么我们有原语及其面向对象的替代方法?

基本类型不是类(与C#不同),因此它们不是的子类Object并且不能被覆盖。

int出于性能方面的考虑,我们有一些原语,出于OO编程的好处,还有一些Object替代物Integer,并且作为次要点,有一个存放实用程序常量和方法(Integer.MAX_VALUE和Integer.toString(int))的好位置。

使用泛型(List<Integer>)可以很容易地看到OO的好处,但不仅限于此,例如:

Number getMeSome(boolean wantInt) {

    if (wantInt) {
        return Integer.MAX_VALUE;
    } else {
        return Long.MAX_VALUE;
    }
}

0

因为它们是不同的类型,为方便起见。性能可能是具有原始类型的原因。


0

一些数据结构只能接受对象,而不能接受原始类型。

示例:HashMap中的键。

有关更多信息,请参见此问题:HashMap和int作为键

还有其他一些很好的理由,例如数据库中的“ int”字段,也可以为NULL。Java中的int不能为null;整数引用可以。自动装箱和拆箱提供了一种避免在转换中来回编写多余代码的功能。


0

ArrayList不支持基本类型,仅支持类。但是我们需要使用基本类型,例如int,double等。

ArrayList<String> strArrayList = new ArrayList<String>(); // is accepted.

ArrayList<int> intArrayList = new ArrayList<int>(); // not accepted.

Integer类将原始类型int的值包装在一个对象中。因此可以接受下面的代码。

ArrayList<Integer> intArrayList = new ArrayList<Integer>(); // is accepted.

我们可以使用add(value)方法添加一个值。要添加一个字符串值,在strArrayList代码中说“ Hello”只是

strArrayList.add("Hello");  

并添加一个int值说54我们可以写

intArrayList.add(54);

但是当我们写intArrayList.add(54); 编译器转换为以下行

intArrayList.add(Integer.valueOf(54)); 

因为intArrayList.add(54)很容易并且在用户方面更容易接受,所以编译器完成了艰辛的工作,那就是“intArrayList.add(Integer.valueOf(54));自动装箱”。

同样,要检索值,我们只需键入intArrayList.get(0)并将编译器转换<code>intArrayList.get(0).intValue();为autoUnboxing。


0

自动装箱:将原始值转换为相应包装器类的对象。

拆箱:将包装器类型的对象转换为其对应的原始值

// Java program to illustrate the concept 
// of Autoboxing and Unboxing 
import java.io.*; 

class GFG 
{ 
    public static void main (String[] args) 
    { 
        // creating an Integer Object 
        // with value 10. 
        Integer i = new Integer(10); 

        // unboxing the Object 
        int i1 = i; 

        System.out.println("Value of i: " + i); 
        System.out.println("Value of i1: " + i1); 

        //Autoboxing of char 
        Character gfg = 'a'; 

        // Auto-unboxing of Character 
        char ch = gfg; 
        System.out.println("Value of ch: " + ch); 
        System.out.println("Value of gfg: " + gfg); 

    } 
} 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.