为什么我们在Java中使用自动装箱和拆箱?
自动装箱是Java编译器在基元types和相应的对象包装类之间进行的自动转换。 例如,将int转换为Integer,double转换为Double等等。 如果转换是另一种方式,则称为拆箱。
那么为什么我们需要它,为什么我们在Java中使用自动装箱和拆箱呢?
完全理解背后的主要原因需要一些背景。
原始类与类
Java中的基本variables包含值 (整数,双精度浮点二进制数等)。 因为这些值可能有不同的长度 ,所以包含它们的variables也可能有不同的长度(考虑float
和double
)。
另一方面, 类variables包含对实例的引用 。 引用通常以许多语言实现为指针(或者与指针非常类似的东西)。 这些东西通常具有相同的大小,而不pipe它们引用的实例的大小( Object
, String
, Integer
等)。
类variables的这个属性使得它们包含的引用可以互换 (在一定程度上)。 这允许我们做我们所说的replace :广义地说, 使用特定types的实例作为另一个相关types的实例(例如,使用String
作为Object
)。
原始variables不能以相同的方式互换 ,也不能与Object
互换 。 这个最明显的原因(但不是唯一的原因)是它们的尺寸差异。 这使得原始types在这方面不方便,但我们仍然需要它们的语言(主要是因为性能的原因)。
generics和types删除
genericstypes是具有一个或多个types参数的types (确切的数字称为generics )。 例如, genericstypes定义 List<T>
有一个types参数T
,它可以是Object
(产生具体types List<Object>
), String
( List<String>
), Integer
( List<Integer>
)等等。
genericstypes比非genericstypes复杂得多。 当它们被引入到Java(在它的初始版本之后)时,为了避免对JVM做出根本的改变,并且可能破坏了与旧的二进制文件的兼容性,Java的创build者决定以最小侵入的方式实现generics:所有具体的types实际上, List<T>
被编译成List<Object>
(对于其他types,绑定可能是Object
以外的其他types的东西,但你明白了)的二进制等价物。 generics和types参数信息在这个过程中丢失了 ,这就是我们称之为types擦除的原因 。
把两个放在一起
现在问题是上述现实的组合:如果List<T>
在所有情况下都变为List<Object>
,那么T
必须始终是可以直接分配给Object
。 其他任何事情都不能被允许。 因为,正如我们之前所说的, int
, float
和double
不能与Object
互换,所以不能有List<int>
, List<float>
或者List<double>
(除非generics的实现更为复杂JVM)。
但是Java提供了像Integer
, Float
和Double
这样的类,它们将这些原语封装在类实例中,使得它们可以被有效地replace为Object
,从而允许genericstypes也间接地处理原语 (因为可以有List<Integer>
, List<Float>
, List<Double>
等)。
从int
创buildInteger
,从float
创buildFloat
等过程被称为装箱(boxing) 。 反过来被称为拆箱 。 因为每次你想要使用它们来装箱原始数据都是不方便的, 有些情况下语言会自动执行这个操作 – 这就是所谓的自动装箱 。
“自动装箱” 用于将原始数据types转换为其包装类对象。 包装类提供了对原始types执行的各种function。 最常见的例子是:
int a = 56; Integer i = a; // Auto Boxing
这是需要的,因为程序员很容易能够直接编写代码,JVM会照顾拳击和拆箱。
当我们使用java.util.Collectiontypes时,自动装箱也会派上用场。 当我们想要创build一个原始types的集合,我们不能直接创build一个原始types的集合,我们只能创build对象的集合。 例如 :
ArrayList<int> al = new ArrayList<int>(); // not supported ArrayList<Integer> al = new ArrayList<Integer>(); // supported al.add(45); //auto Boxing
包装类
每个Java的8个基本types(byte,short,int,float,char,double,boolean,long)都有一个独立的Wrapper类。 这些Wrapper类具有预定义的方法,用于对基本数据types执行有用的操作。
使用包装类
String s = "45"; int a = Integer.parseInt(s); // sets the value of a to 45.
Wrapper类提供了许多有用的函数。 看看这里的java文档
拆箱与自动装箱相反,我们将包装类对象转换回原始types。 这是由JVM自动完成的,以便我们可以使用包装类进行某些操作,然后将它们转换回原始types,因为原语会导致int处理更快。 例如 :
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 .
原始(非对象)types有效率的理由。
基本typesint, boolean, double
是直接数据,而Object
是引用。 因此,字段(或variables)
int i; double x; Object s;
将需要本地内存4 + 8 + 8? 对象只有存储器的引用(地址)被存储。
使用对象包装器Integer, Double
和其他,可以引入一个间接引用,在堆内存中引用一些Integer / Double实例。
为什么拳击需要?
这是一个相对范围的问题。 在未来的java计划能够有一个ArrayList<int>
,提升原始types。
答:现在ArrayList只能用于Object,为对象引用保留空间,同时也pipe理垃圾回收。 因此genericstypes是对象的孩子。 所以如果你想要一个浮点值的ArrayList,需要在Double对象中包装一个double。
在这里,Java与传统的C ++及其模板不同:C ++类vector<string>, vector<int>
将创build两个编译产品。 Javadevise有一个ArrayList.class,不需要为每个参数types创build一个新的编译产品。
所以,如果没有装箱的话,对于每一个参数types的出现,都需要编译类。 在concreto中:每个集合或容器类都需要一个Object,int,double,boolean的版本。 Object的版本将处理所有的子类。
事实上,在Java SE中对IntBuffer,CharBuffer,DoubleBuffer等已经存在这种多样化的需求,它们在int,char,double上运行。 这是通过从一个共同的源生成这些来源哈克的方式解决。
因为他们是不同的types,并作为一个方便。 性能可能是原始types的原因。
一些数据结构只能接受对象,而不能接受原始types。
例如:HashMap中的键。
看到这个问题更多: HashMap和int作为关键
还有其他很好的理由,例如数据库中的“int”字段,也可以是NULL。 Java中的int不能为null; 一个Integer引用可以。 自动装箱和拆箱提供了一种方法来避免在来回转换中编写无关的代码。
为什么我们有(不)拳击?
在我们混合原语和面向对象(Object Oriented,OO)的替代scheme时,编写代码更加舒适/减less冗长。
为什么我们有原始人和他们的面向对象的select?
原始types不是类(与C#不同),因此它们不是Object
子类,不能被覆盖。
我们有像int
这样的基本性能的原因,和Object
替代物像Integer
的OO编程的好处,作为一个小点,有一个良好的工具常量和方法的位置(Integer.MAX_VALUE和Integer.toString(int)
) 。
使用generics( List<Integer>
)可以很容易地看到面向对象的好处,但不仅限于此,例如:
Number getMeSome(boolean wantInt) { if (wantInt) { return Integer.MAX_VALUE; } else { return Long.MAX_VALUE; } }
从JDK 5开始,java增加了两个重要的function:autoboxing和autounboxing。 AutoBoxing是这样一个过程,只要需要这样一个对象,基本types就会自动封装在等价的包装器中。 您不必显式构造一个对象。 自动拆箱是指一个封装对象的值在需要时自动从types封装器提取的过程。 您不需要调用诸如intValue()或doubleValue()之类的方法 。
自动装箱和自动拆箱的添加极大地简化了书写algorithm ,消除了手动装箱和拆箱的诱饵。 避免错误也是有帮助的。 只对对象进行操作的generics也非常重要。 最后,自动装箱方便了集合框架的工作 。