Autoboxing是Java编译器自动转换 原始类型与其对应的对象包装器之间 类。例如,将int转换为Integer,将double转换为a 双,等等。如果转换是另一种方式,那就是 称为拆箱。
那么为什么我们需要它呢?为什么我们在Java中使用自动装箱和拆箱?
答案 0 :(得分:147)
需要一些背景来充分理解背后的主要原因。
Java中的原始变量包含值(整数,双精度浮点二进制数等)。由于these values may have different lengths,包含它们的变量也可能有不同的长度(考虑float
与double
)。
另一方面,类变量包含对实例的引用。引用通常在许多语言中实现为指针(或与指针非常相似的东西)。这些内容通常具有相同的大小,无论它们引用的实例的大小如何(Object
,String
,Integer
等等。
类变量的这个属性使它们包含的引用可以互换(在一定程度上)。这允许我们执行我们称之为替换:广义上说, to use an instance of a particular type as an instance of another, related type (例如,使用String
作为Object
)。
原始变量不可互换以相同的方式,彼此既不与Object
也不相同。最明显的原因(但不是唯一的原因)是它们的尺寸差异。这使得原始类型在这方面不方便,但我们仍然需要它们的语言(原因主要归结为性能)。
通用类型是具有一个或多个类型参数的类型(确切的数字称为 generic arity )。例如,泛型类型定义 List<T>
具有类型参数T
,可以是Object
(生成具体类型 { {1}}),List<Object>
(String
),List<String>
(Integer
)等等。
通用类型比非通用类型复杂得多。当它们被引入Java(在其初始版本之后)时,为了避免对JVM进行根本性更改并可能破坏与旧二进制文件的兼容性, Java的创建者决定以最少侵入性的方式实现泛型类型: / strong>事实上,所有具体类型的List<Integer>
都被编译为(二进制等价物)List<T>
(对于其他类型,绑定可能不是List<Object>
,但是你明白这点)。 此过程中会丢失通用的arity和类型参数信息,这就是我们称之为type erasure的原因。
现在问题是上述现实的结合:如果Object
在所有情况下变为List<T>
,那么 List<Object>
必须始终是可以直接分配到的类型T
即可。其他任何东西都不允许。正如我们之前所说,Object
,int
和float
不能与double
互换,因此不能有Object
,{{1 }或List<int>
(除非JVM中存在明显更复杂的泛型实现)。
但Java提供类似List<float>
,List<double>
和Integer
的类型,它们将这些原语包装在类实例中,使它们可以有效地替换为Float
,因此允许泛型类似于间接使用原语(因为你可以拥有Double
,Object
,List<Integer>
等等。
从List<Float>
创建List<Double>
,从Integer
创建int
等等的过程称为 boxing 。反向称为拆箱。因为每次要将它们用作Float
时必须填充原语是不方便的,有时语言会自动执行此操作 - that's called autoboxing。
答案 1 :(得分:13)
自动装箱 使用 将原始数据类型转换为其包装类对象。 包装类提供了对基本类型执行的各种功能。最常见的例子是:
int a = 56;
Integer i = a; // Auto Boxing
需要 因为程序员很容易直接编写代码而JVM会负责Boxing和Unboxing。
当我们使用java.util.Collection类型时,Auto Boxing也派上用场。当我们想要创建一个基本类型的集合时,我们不能直接创建一个基本类型的集合,我们只能创建对象的集合。例如:
ArrayList<int> al = new ArrayList<int>(); // not supported
ArrayList<Integer> al = new ArrayList<Integer>(); // supported
al.add(45); //auto Boxing
包装类
每个Java的原始类型(byte,short,int,float,char,double,boolean,long)都有一个单独的Wrapper类与它们相关联。这些Wrapper类具有预定义的方法,用于对原始数据类型执行有用的操作。
使用包装类
String s = "45";
int a = Integer.parseInt(s); // sets the value of a to 45.
Wrapper类提供了许多有用的功能。查看java docs here
取消装箱与Auto Boxing相反,我们将包装类对象转换回其原始类型。这是由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 .
答案 2 :(得分:4)
原始(非对象)类型在效率上有合理性。
原始类型int, boolean, double
是立即数据,而Object
是引用。因此字段(或变量)
int i;
double x;
Object s;
需要本地内存4 + 8 + 8吗?对象的位置只存储存储器的引用(地址)。
使用Object包装器Integer, Double
和其他人,会引入一个间接,引用堆内存中的某个Integer / Double实例。
为什么需要拳击?
这是一个相对范围的问题。在未来的java中,计划能够拥有ArrayList<int>
,解除原始类型。
答案: 目前,ArrayList仅适用于Object,为对象引用保留空间,同样管理垃圾收集。因此,泛型类型是Object子元素。 因此,如果想要一个浮点值的ArrayList,则需要在Double对象中包含double。
这里的Java与传统的C ++及其模板不同:C ++类vector<string>, vector<int>
将创建两个编译产品。 Java设计有一个ArrayList.class,不需要为每个参数类型提供新的编译产品。
因此,如果没有装箱到对象,则需要为每次出现的参数类型编译类。在concreto中:每个集合或容器类都需要Object,int,double,boolean的版本。 Object的版本将处理所有子类。
事实上,对于IntBuffer,CharBuffer,DoubleBuffer等的Java SE中已经存在对这种多样化的需求,它们在int,char,double上运行。它是通过从一个普通的源生成这些来源以一种黑客的方式解决的。
答案 3 :(得分:4)
从JDK 5开始,java增加了两个重要功能:autoboxing和autounboxing。 AutoBoxing 是在需要这样的对象时,基本类型自动封装在等效包装器中的过程。您不必显式构造对象。 自动取消装箱是在需要值时自动从类型包装器中提取封装对象的值的过程。您无需调用 intValue()或 doubleValue()等方法。
自动装箱和自动拆箱的添加极大地简化了编写算法,消除了手动装箱和取消装箱值的诱饵。 避免错误也很有帮助。非常重要的 for generics ,只对对象进行操作。最后,自动装箱有助于使用收藏框架。
答案 4 :(得分:1)
为什么我们有(联合国)拳击?
编写代码,我们将基元和面向对象(OO)的替代方案混合起来更舒适/更简洁。
为什么我们有基元及其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;
}
}
答案 5 :(得分:0)
因为它们是不同的类型,并且为了方便。性能可能是原始类型的原因。
答案 6 :(得分:0)
某些数据结构只能接受对象,不能接受基本类型。
示例:HashMap中的键。
有关详情,请参阅此问题:HashMap and int as key
还有其他很好的理由,例如数据库中的“int”字段,也可能是NULL。 Java中的int不能为null;整数引用可以。自动装箱和拆箱提供了一种工具,可以避免在转换中来回编写无关代码。
答案 7 :(得分: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();
,这是自动拆箱。
答案 8 :(得分: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);
}
}