为了完全理解其主要原因,需要一些背景知识。
Java中的基本变量包含值(整数、双精度浮点二进制数等)。因为这些值可能有不同的长度,包含它们的变量也可能有不同的长度(考虑float
与double
)。
另一方面,类变量包含对实例的引用。在许多语言中,引用通常被实现为指针(或非常类似于指针的东西)。这些东西通常具有相同的大小,而不管它们所引用的实例的大小(例如Object
、String
、Integer
等)。
String
用作Object
)。
原始变量并不具备同样的可互换性,无论是彼此之间还是与Object
之间。这种情况最明显的原因(但不是唯一的原因)是它们的大小差异。这使得原始类型在这方面不太方便,但我们仍然需要它们存在于语言中(主要出于性能方面的原因)。
通用类型是具有一个或多个类型参数的类型(确切的数量称为泛型度)。例如,List<T>
的通用类型定义具有类型参数 T
,可以是 Object
(产生一个具体类型 List<Object>
),String
(List<String>
),Integer
(List<Integer>
) 等等。
List<T>
的具体类型实际上都被编译成(二进制等效的)List<Object>
(对于其他类型,绑定可能是其他内容而不是Object
,但您明白我的意思)。在这个过程中,泛型度和类型参数信息丢失了,这就是我们称之为类型擦除的原因。List<T>
变成了 List<Object>
,那么 T
必须始终是一种可以直接赋值给 Object
的类型。其他任何类型都不被允许。由于前面所说的,int
、float
和 double
不能与 Object
相互替换,所以就不能有 List<int>
、List<float>
或 List<double>
(除非 JVM 中存在更为复杂的泛型实现)。Integer
、Float
和 Double
这样的类型,它们将这些原始类型封装在类实例中,从而使它们有效地可替换为 Object
,因此允许泛型类型间接地使用基本类型(因为你可以有 List<Integer>
、List<Float>
、List<Double>
等等)。
将一个int
转换为Integer
,将一个float
转换为Float
等过程称为装箱(boxing)。相反的过程称为拆箱(unboxing)。由于每次想要将基本类型转换为Object
时都必须进行装箱操作,这样做很不方便,因此在某些情况下,语言会自动执行此操作 - 这就是所谓的自动装箱(autoboxing)。
自动装箱用于将原始数据类型转换为它们的包装器类对象。 包装器类提供了广泛的功能来执行原始类型。最常见的示例是:
int a = 56;
Integer i = a; // Auto Boxing
由于程序员可以直接编写代码,而JVM会处理装箱和拆箱,因此需要它。
当我们使用java.util.Collection类型时,自动装箱也非常方便。当我们想要创建原始类型的Collection时,我们无法直接创建原始类型的Collection,只能创建对象类型的Collection。例如:
ArrayList<int> al = new ArrayList<int>(); // not supported
ArrayList<Integer> al = new ArrayList<Integer>(); // supported
al.add(45); //auto Boxing
包装类
Java的8种基本类型(byte、short、int、float、char、double、boolean、long)每一种都有一个对应的包装类,这些包装类中预定义了许多处理基础数据类型的实用方法。
使用包装类
String s = "45";
int a = Integer.parseInt(s); // sets the value of a to 45.
包装类提供了许多有用的函数。在这里查看java文档:这里
拆箱(Unboxing)是与自动装箱相反的过程,它将包装类对象转换回其基本数据类型。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
是即时数据,而对象
是引用。因此字段(或变量)
int i;
double x;
Object s;
为什么我们需要(拆箱和装箱)?
为了使混合使用基本类型和它们的面向对象(OO)替代品的代码编写更加舒适/简洁。
为什么我们有基本类型和它们的OO替代品?
基本类型不是类(与C#不同),因此它们不是Object
的子类,也不能被覆盖。
我们有像int
这样的基本类型是出于性能原因,而Integer
等Object
替代品则是为了享受OO编程的好处,并且作为一个小点,为实用程序常量和方法提供一个良好的位置(如Integer.MAX_VALUE和Integer.toString(int)
)。
OO的好处最容易在泛型(List<Integer>
)中看到,但不仅限于此,例如:
Number getMeSome(boolean wantInt) {
if (wantInt) {
return Integer.MAX_VALUE;
} else {
return Long.MAX_VALUE;
}
}
ArrayList不支持原始类型,只支持类。但我们需要使用原始类型,例如int、double等。
ArrayList<String> strArrayList = new ArrayList<String>(); // is accepted.
ArrayList<int> intArrayList = new ArrayList<int>(); // not accepted.
整型类将基本类型int的值封装在一个对象中,因此下面的代码是被接受的。
ArrayList<Integer> intArrayList = new ArrayList<Integer>(); // is accepted.
strArrayList.add("Hello");
intArrayList.add(54);
但是当我们写intArrayList.add(54)时,编译器会将其转换为以下行:
intArrayList.add(Integer.valueOf(54));
另一个特殊情况是,
Integer intval = null;
int toPrimitive = intval;
System.out.println(toPrimitive);
针对上述情况,我们遇到了NullPointerException
。这意味着我们可以捕获NPE。
因为它们是不同的类型,并且作为一种便利。性能很可能是拥有原始类型的原因。
// 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);
}
}
Integer
有parseInt
方法,而int
没有。 :) - Vishal ZanzrukiaList<Integer>
,但你不能有List<int>
。 - Vishal Zanzrukia