Java自动装箱(autoboxing)和自动拆箱(autounboxing)介绍
先回顾一下 Java 中的基本数据类型和包装类。
基本数据类型(Primitive Data Types): Java 提供了一组基本数据类型,有8种基本数据类型:byte、short、int、long、float、double、char和boolean。
数据类型 | 取值范围 |
byte | -128 ~ 127 |
short | -32786 ~ 32767 |
int | -4294967296 ~ 4294967295 |
long | -2^64^ ~ 2^64^ -1 |
float | 3.4e-038 ~ 3.4e+038 |
double | 1.7e-308 ~ 1.7e+308 |
char | \u0000 ~ \uffff |
boolean | true 、false |
包装类(Wrapper Classes): 为了使基本数据类型具备面向对象的特性,Java 提供了对应的包装类,Byte、Short、Integer、Long、Float、Double、Character和Boolean。这些包装类用于将基本数据类型封装为对象,以便进行更多的操作。
Java自动装箱和拆箱的意思就是 Java 编译器自动处理基本数据类型与包装类之间的转换,使程序员无需手动编写繁琐的代码。自动装箱和自动拆箱也可以简称为装箱和拆箱。
Java中基础数据类型与它们对应的包装类见下表(共8种):
原始类型 | 包装类型 |
boolean | Boolean |
byte | Byte |
char | Character |
float | Float |
int | Integer |
long | Long |
short | Short |
double | Double |
当表格中左边列出的基础类型与它们的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱。
- 进行 = 赋值操作(装箱或拆箱)
- 进行+,-,*,/混合运算 (拆箱)
- 进行>,<,==比较运算(拆箱)
- 调用equals进行比较(装箱)
- ArrayList,HashMap等集合类 添加基础类型数据时(装箱)
一个自动拆箱的示例:
Integer i = new Integer(10);
int j = i;
在这个示例中,将一个封装类型的变量i赋值给一个基本类型的变量j,编译器会自动将i转换为int类型的数值10,然后赋值给j。
一个自动装箱的示例:
int i = 10;
Integer j = i;
在这个示例中,将一个基本类型的变量i赋值给一个封装类型的变量j,编译器会自动将i转换为Integer类型的对象,然后赋值给j。
在Java中,自动装箱(Autoboxing)和自动拆箱(Unboxing)是从Java 5开始引入的概念,它们是编译器的特性,使得原始类型(primitive types)和它们对应的包装类(wrapper classes)之间的转换自动化。
public class Demo {
public static void main(String[] args) {
//自动装箱
Integer total = 99;
//自动拆箱
int totalprim = total;
}
}
自动装箱(Autoboxing)
自动装箱是指将原始类型自动转换为对应的包装类对象的过程。当编译器遇到一个原始类型的值,而该值需要被当作一个对象来处理时,编译器会自动将原始类型的值转换为对应的包装类对象。这通常发生在:
- 将原始类型赋值给对应的包装类变量时。
- 将原始类型传递给期望对象的方法时。
- 将原始类型添加到集合中,如List或Set,这些集合只能存储对象。
例如:
Integer integerObject = 10; // 自动装箱,int转换为Integer
List<Integer> listOfIntegers = new ArrayList<>();
listOfIntegers.add(20); // 自动装箱,int转换为Integer
在上面的例子中,数字10和20是原始类型int,它们被自动装箱成为Integer对象,然后被存储在对应的变量和集合中。
自动拆箱(Unboxing)
自动拆箱是指将包装类对象自动转换为对应的原始类型值的过程。当编译器遇到一个包装类对象,而该对象需要被当作一个原始类型的值来处理时,编译器会自动将包装类对象转换为原始类型的值。这通常发生在:
- 将包装类对象赋值给对应的原始类型变量时。
- 将包装类对象传递给期望原始类型的方法时。
- 在算术运算或比较运算中使用包装类对象时。
例如:
Integer integerObject = new Integer(30);
int i = integerObject; // 自动拆箱,Integer转换为int
在上面的例子中,integerObject是一个Integer对象,它被自动拆箱成为int原始类型的值i。
注意事项
虽然自动装箱和拆箱使得原始类型和包装类之间的转换变得简单,但它们也可能引入一些问题:
- 性能开销:装箱操作涉及到对象的创建,而拆箱操作可能涉及到类型检查和值的提取。频繁的装箱和拆箱可能会对性能产生负面影响,特别是在大量计算的场景中。
- 空指针异常(NullPointerException):自动拆箱时如果包装类对象为null,则会抛出空指针异常。
- 等价性问题:包装类对象是引用类型,因此在比较时,使用==运算符会比较引用而不是值。为了比较值,应该使用equals()方法。
例如:
Integer a = 1000, b = 1000;
System.out.println(a == b); // false,因为比较的是引用
System.out.println(a.equals(b)); // true,因为比较的是值
但是,对于在-128到127之间的小整数值,Java会缓存这些对象的实例,所以使用==运算符比较这些值时可能会得到true。
Integer c = 100, d = 100;
System.out.println(c == d); // true,因为小整数值的实例被缓存
了解这些细节对于编写正确和高效的Java代码是非常重要的。
进一步学习了解可参见:
如何理解Java中的自动拆箱和自动装箱?https://www.cnblogs.com/niceyoo/p/12977541.html
详解Java的自动装箱与拆箱(Autoboxing and unboxing)https://www.cnblogs.com/wang-yaz/p/8516151.html
深入探究Java自动拆箱与装箱的实现原理https://cloud.tencent.com/developer/article/2361504
附录、C++、Java、Python、JavaScript等语言中都有自动装箱和自动拆箱概念吗?
自动装箱和自动拆箱是编程语言中的两个概念,主要与类型系统有关,尤其是在处理基本数据类型和它们的对象表示(包装类)时出现。
- C++:C++中没有自动装箱和自动拆箱的概念。C++是一种静态类型语言,不提供基本数据类型和对象之间的自动转换。C++中有类似的概念,比如构造函数和类型转换运算符,可以用来在对象和基本数据类型之间进行转换,但这些都需要显式定义。
- Java:Java中有自动装箱和自动拆箱的概念。从Java 5开始,Java提供了自动装箱和自动拆箱功能,使得基本数据类型和它们的包装类(如int和Integer、double和Double等)之间可以自动转换。
- Python:Python中没有自动装箱和自动拆箱的概念,因为Python是一种动态类型语言,它不区分基本数据类型和对象类型。在Python中,数字、字符串等都是对象。
- JavaScript:JavaScript也是一种动态类型语言,它有类似自动装箱的行为,但通常不这么称呼。在JavaScript中,基本数据类型(如数字、字符串、布尔值)在需要时会被自动封装为对象(例如,当你调用基本类型的方法时)。然而,这个过程是透明的,开发者通常不需要关心这个自动转换。
- C#语言的自动装箱和自动拆箱与Java语言中的这两个概念非常类似。在C#中,装箱和拆箱是用于在值类型(如结构体和基本数据类型)和引用类型(如object或者接口类型)之间转换的过程。
总的来说,自动装箱和自动拆箱主要是Java中的概念,而在在其他编程语言中,这些概念要么不存在,要么以不同的形式存在。