一文带你理解Java中自动装箱和拆箱

34 篇文章 2 订阅

Java中自动装箱和拆箱

装箱(Boxing),也称为包装(Wrapper),是在对象中放置原语类型(primitive type)的过程,以便原语(primitive)可以作为引用对象使用。

这里的primitive type就是Java里面的基本类型,所有的基本类型都有一个与之对应的类。例如,Integer类对应基本类型int。

通常,这些类称为包装器(wrapper)。这些对象包装器类拥有很明显的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6个类派生于公共的超类Number)。

对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。

自动装箱是指通过类型转换(隐式或显式)从值类型中获取引用类型,这部分工作是编译器帮我们来完成的。

我们看一个常见的例子,比如我们创建一个int类型的ArrayList(因为ArrayList的泛型是不允许基本类型的,这里只能使用它们包装类),我们给ArrayList添加元素,再从里面获取元素,一般是这么写的:

// int类型的自动装箱和拆箱
ArrayList<Integer> integerArrayList = new ArrayList<>();
integerArrayList.add(1);
int i = integerArrayList.get(0);

这里分别触发了自动装箱和自动拆箱,这里的add操作触发了一次自动装箱操作,将int转化为Integer;接着从ArrayList里面获取元素,由于我们的目标变量类型是基本类型int,但获取到的元素类型是Integer,所以编译器在这里帮我们做了拆箱的操作。

通过字节码查看自动装箱和自动拆箱是如何实现的

我们经常说自动装箱、自动拆箱,到底是如何个自动法,我们来一个眼见为实,通过查看java代码生成的字节码来看下编译器对我们的代码做了什么。

查看字节码的方式

这里介绍两种查看字节码的方式:
第一种,通过javac和javap查看:先通过javac将.java代码编译成.class字节码,然后通过javap分析字节码。

(base) tinytongtongdeMacBook-Pro% javac TestAutoWrapper.java
(base) tinytongtongdeMacBook-Pro% javap -verbose TestAutoWrapper

这样你就能看到你的字节码信息了。

第二种,通过IDE插件ASM Bytecode Outline来查看,具体操作方式见插件说明。

查看自动装箱和拆箱的字节码
public static void main(String[] args) {
    // int类型的自动装箱和拆箱
    ArrayList<Integer> integerArrayList = new ArrayList<>();
    integerArrayList.add(1);// 自动装箱
    int i = integerArrayList.get(0);// 自动拆箱
}

我们生成这段java代码的字节码,核心部分如下:

// access flags 0x9
  public static main([Ljava/lang/String;)V
   ...
   L1
    LINENUMBER 15 L1
    ALOAD 1
    ICONST_1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L2
    LINENUMBER 16 L2
    ALOAD 1
    ICONST_0
    INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;
    CHECKCAST java/lang/Integer
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ISTORE 2
   ...

L1部分中的倒数第二行,INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)ZINVOKEVIRTUAL指令表示一个虚方法调用,这里具体就是我们java代码中的integerArrayList.add(1);,自动装箱发生在哪呢?就在它上面,INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;INVOKESTATIC表示静态方法调用,这里对应的Java语句就是Integer#valueOf()方法。

接下来我们看下自动装箱对应的字节码,也就是L2部分,先看INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;,它对应的java代码是integerArrayList.get(0),表示从ArrayList里面获取到Integer类型对象。自动拆箱发生在下面,就是NVOKEVIRTUAL java/lang/Integer.intValue ()I这条指令,它对应的java方法是Integer#intValue()方法。

看到这里相信大家对自动装箱拆箱有一个比较具体的认识了,说白了就是编译器会根据情况替我们做一些工作,通过插入字节码指令来替我们完成装箱和拆箱操作。int对应的装箱方法是Integer#valueOf,拆箱方法是Integer#intValue()

自动装箱和拆箱的触发时机

我们接着讲下自动装箱和拆箱的触发时机,具体如下:

* 进行 = 赋值操作(装箱或拆箱)
* 进行+,-,*,/混合运算 (拆箱)
* 进行>,<,==比较运算(拆箱)
* 调用equals进行比较(装箱)
* ArrayList,HashMap等集合类 添加基础类型数据时(装箱)
基本类型自动装箱和拆箱方法总结
基本类型包装类型装箱方法拆箱方法
intIntegerInteger#valueOf()Integer#intValue()
longLongLong.valueOf()Long.longValue()
byteByteByte.valueOf()Byte.byteValue()
shortShortShort.valueOf()Short.shortValue()
floatFloatFloat.valueOf()Float.floatValue()
doubleDoubleDouble.valueOf()Double.doubleValue()
charCharacterCharacter.valueOf()Character.charValue()
booleanBooleanBoolean.valueOf()Boolean.booleanValue()

感兴趣的同学可以自己试下各种操作下,自动装箱和拆箱的表现,查看对应的字节码即可。

参考

Wikiedia:Object type (object-oriented programming)
5分钟彻底理解-Java自动装箱、拆箱

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spark是一个快速通用的集群计算框架,它可以处理大规模数据,并且具有高效的内存计算能力。Spark可以用于各种计算任务,包括批处理、流处理、机器学习等。本文将你了解Spark计算框架的基本概念和使用方法。 一、Spark基础概念 1. RDD RDD(Resilient Distributed Datasets)是Spark的基本数据结构,它是一个分布式的、可容错的、不可变的数据集合。RDD可以从Hadoop、本地文件系统等数据源读取数据,并且可以通过多个转换操作(如map、filter、reduce等)进行处理。RDD也可以被持久化到内存,以便下次使用。 2. Spark应用程序 Spark应用程序是由一个驱动程序和多个执行程序组成的分布式计算应用程序。驱动程序是应用程序的主要入口点,它通常位于用户的本地计算机上,驱动程序负责将应用程序分发到执行程序上并收集结果。执行程序是运行在集群节点上的计算单元,它们负责执行驱动程序分配给它们的任务。 3. Spark集群管理器 Spark集群管理器负责管理Spark应用程序在集群的运行。Spark支持多种集群管理器,包括Standalone、YARN、Mesos等。 二、Spark计算框架使用方法 1. 安装Spark 首先需要安装Spark,可以从Spark官网下载并解压缩Spark安装包。 2. 编写Spark应用程序 编写Spark应用程序通常需要使用Java、Scala或Python编程语言。以下是一个简单的Java代码示例,用于统计文本文件单词的出现次数: ```java import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import java.util.Arrays; import java.util.Map; public class WordCount { public static void main(String[] args) { SparkConf conf = new SparkConf().setAppName("WordCount").setMaster("local"); JavaSparkContext sc = new JavaSparkContext(conf); JavaRDD<String> lines = sc.textFile("input.txt"); JavaRDD<String> words = lines.flatMap(line -> Arrays.asList(line.split(" ")).iterator()); Map<String, Long> wordCounts = words.countByValue(); for (Map.Entry<String, Long> entry : wordCounts.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } sc.stop(); } } ``` 3. 运行Spark应用程序 将编写好的Spark应用程序打包成jar包,并通过以下命令运行: ```bash spark-submit --class WordCount /path/to/wordcount.jar input.txt ``` 其,--class参数指定应用程序的主类,后面跟上打包好的jar包路径,input.txt是输入文件的路径。 4. 查看运行结果 Spark应用程序运行完毕后,可以查看应用程序的输出结果,例如上述示例的单词出现次数。 以上就是Spark计算框架的基本概念和使用方法。通过学习Spark,我们可以更好地处理大规模数据,并且提高计算效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tinyvampirepudge

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值