自动装箱是Java编译器在原始类型及其对应的对象包装器类之间进行的自动转换。例如,将int转换为Integer,将double转换为Double,依此类推。如果转换过程相反,这称为拆箱。
那么,为什么我们需要它?为什么要在Java中使用自动装箱和拆箱呢?
自动装箱是Java编译器在原始类型及其对应的对象包装器类之间进行的自动转换。例如,将int转换为Integer,将double转换为Double,依此类推。如果转换过程相反,这称为拆箱。
那么,为什么我们需要它?为什么要在Java中使用自动装箱和拆箱呢?
Integer
有parseInt
方法。int
不具有。:)
List<Integer>
,但你不能List<int>
。
Answers:
需要一些上下文来充分理解其背后的主要原因。
Java中的原始变量包含值(整数,双精度浮点二进制数等)。由于这些值的长度可能不同,因此包含它们的变量的长度也可能不同(请考虑float
与double
)。
另一方面,类变量包含对实例的引用。引用通常以多种语言实现为指针(或与指针非常相似的东西)。这些东西通常具有相同的大小,而不管它们指的是(实例的大小Object
,String
,Integer
,等)。
类变量的此属性使它们包含的引用可以互换(一定程度上)。这使我们能够执行所谓的替换:广义上讲,是将特定类型的实例用作另一种相关类型的实例(例如,将aString
用作Object
)。
原始变量不能以相同的方式互换,彼此之间也不能互换Object
。最明显的原因(但不是唯一原因)是它们的大小差异。这使得原始类型在这方面不方便,但是我们仍然需要语言中的它们(由于主要归结为性能的原因)。
泛型类型是具有一个或多个类型参数的类型(确切的数字称为泛型arity)。例如,通用类型定义 List<T>
具有类型参数T
,可以是Object
(产生具体类型 List<Object>
),String
(List<String>
),Integer
(List<Integer>
)等。
泛型类型比非泛型类型复杂得多。当他们被介绍到Java(首次发行)之后,为了避免到JVM根本变化,并与旧的二进制可能破坏兼容性,Java的创作者决定在最小侵入性的方式来实现泛型类型:所有的具体类型List<T>
实际上,已被编译为(的二进制等效项)List<Object>
(对于其他类型,边界可能不是Object
,但您明白了)。泛型和类型参数信息在此过程中丢失,这就是为什么我们将其称为类型擦除。
现在的问题是上述现实的结合:如果在所有情况下都List<T>
变为List<Object>
,则T
必须始终是可以直接分配给的类型Object
。不允许其他任何事情。因为,正如我们之前所说,int
,float
并且double
不与互换Object
,不能有一个List<int>
,List<float>
或List<double>
(除非仿制药的显著更复杂的实现JVM中的存在)。
但是Java提供喜欢的类型Integer
,Float
以及Double
其在类实例包装这些原语,使他们有效地作为替代Object
,从而使泛型类型与原语间接地工作,以及(因为你可以有List<Integer>
,List<Float>
,List<Double>
等)。
创建的过程Integer
,从一个int
,一个Float
从float
等,被称为拳击。相反的称为拆箱。因为每次使用原始语言都必须对原始语言进行装箱Object
是很不方便的,所以在某些情况下该语言会自动执行此操作-这称为autoboxing。
自动拳击被用来以原始数据类型转换为自己的包装类对象。包装器类提供了对原始类型执行的多种功能。最常见的示例是:
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 .
原始(非对象)类型在效率上有合理性。
基本类型int, boolean, double
是立即数据,而Object
s是引用。因此,字段(或变量)
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进行操作。通过从常见来源生成这些来源,以一种不可靠的方式解决了该问题。
我们为什么要装箱?
使我们在混合原语和其面向对象(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;
}
}
一些数据结构只能接受对象,而不能接受原始类型。
示例:HashMap中的键。
有关更多信息,请参见此问题:HashMap和int作为键
还有其他一些很好的理由,例如数据库中的“ int”字段,也可以为NULL。Java中的int不能为null;整数引用可以。自动装箱和拆箱提供了一种避免在转换中来回编写多余代码的功能。
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。
自动装箱:将原始值转换为相应包装器类的对象。
拆箱:将包装器类型的对象转换为其对应的原始值
// 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);
}
}