1.本文从字节码层面来探究,switch的case变量值为java枚举Enum时,编译器究竟做了什么工作。

2.源码如下

枚举类Color

public enum Color {RED,BLUE,YELLOW
}

switch中使用Color

public class MySwitchTest {int chooseColor(Color c){switch (c){case RED:return 5;case RED:return 6;case YELLOW:return 7;default:return 8;}}
}

3.先看结果,javac编译后,MySwitchTest生成了一个内部类MySwitchTest$1,反编译后的MySwitchTest的代码大致如下:

public class MySwitchTest {int chooseColor(Color c){int a = MySwitchTest$1.$SwitchMap$Color[c.ordinal];switch (a){case 1:return 5;case 2:return 6;case 3:return 7;default:return 8;}}static class MySwitchTest$1 {static final int[] $SwitchMap$Color;static {Color[] values = Color.values();int length = values.length;$SwitchMap$Color = new int[length];try {$SwitchMap$Color[Color.RED.ordinal()] = 1; //和switch中的case 1对应} catch (NoSuchFieldError e) {}try {$SwitchMap$Color[Color.BLUE.ordinal()] = 2; //和switch中的case 2对应} catch (NoSuchFieldError e) {}try {$SwitchMap$Color[Color.YELLOW.ordinal()] = 3; //和switch中的case 3对应} catch (NoSuchFieldError e) {}}}
}

4.我们来看MySwitchTest.java编译后生成的class文件,除了一个MySwitchTest.class外,还生成了一个MySwitchTest$1.class文件,MySwitchTest$1是MySwitchTest的一个内部类,是编译器javac在编译MySwitchTest.java时自动生成的。

运行 javap -v -p MySwitchTest\$1.class,可以得到内部类的字节码如下

Classfile /C:/workspace/test/target/classes/MySwitchTest$1.classLast modified 2019-12-16; size 619 bytesMD5 checksum 26074600ed9a7634433a37fd542bc348Compiled from "MySwitchTest.java"
class MySwitchTest$1minor version: 0major version: 52flags: ACC_SUPER, ACC_SYNTHETIC
Constant pool:
...
{static final int[] $SwitchMap$Color;descriptor: [Iflags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETICstatic {};descriptor: ()Vflags: ACC_STATICCode:stack=3, locals=1, args_size=00: invokestatic  #1                  // Method Color.values:()[LColor;3: arraylength4: newarray       int6: putstatic     #2                  // Field $SwitchMap$Color:[I9: getstatic     #2                  // Field $SwitchMap$Color:[I12: getstatic     #3                  // Field Color.RED:LColor;15: invokevirtual #4                  // Method Color.ordinal:()I18: iconst_119: iastore20: goto          2423: astore_024: getstatic     #2                  // Field $SwitchMap$Color:[I27: getstatic     #6                  // Field Color.BLUE:LColor;30: invokevirtual #4                  // Method Color.ordinal:()I33: iconst_234: iastore35: goto          3938: astore_039: getstatic     #2                  // Field $SwitchMap$Color:[I42: getstatic     #7                  // Field Color.YELLOW:LColor;45: invokevirtual #4                  // Method Color.ordinal:()I48: iconst_349: iastore50: goto          5453: astore_054: return
}
SourceFile: "MySwitchTest.java"
EnclosingMethod: #22.#0                 // MySwitchTest
InnerClasses:static #8; //class MySwitchTest$1

字节码里省略了常量池的内容,以及静态块static{}的一些属性,我们从上依次分析一些重要的内容。

4.1

class MySwitchTest$1minor version: 0major version: 52flags: ACC_SUPER, ACC_SYNTHETIC

major version: 52表示生成这段字节码的jdk版本是8。flags:acc_synthentic表示这个类是编译器自动生成的。

4.2

  static final int[] $SwitchMap$Color;descriptor: [Iflags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

从这一段很容易看出,MySwitchTest$1有一个字段int[] $SwitchMap$Color,flags:ACC_SYNTHETIC 表示该字段是编译器自动生成的。

4.3

static {};descriptor: ()Vflags: ACC_STATICCode:stack=3, locals=1, args_size=00: invokestatic  #1                  // Method Color.values:()[LColor;3: arraylength4: newarray       int6: putstatic     #2                  // Field $SwitchMap$Color:[I9: getstatic     #2                  // Field $SwitchMap$Color:[I12: getstatic     #3                  // Field Color.RED:LColor;15: invokevirtual #4                  // Method Color.ordinal:()I18: iconst_119: iastore20: goto          2423: astore_024: getstatic     #2                  // Field $SwitchMap$Color:[I27: getstatic     #6                  // Field Color.BLUE:LColor;30: invokevirtual #4                  // Method Color.ordinal:()I33: iconst_234: iastore35: goto          3938: astore_039: getstatic     #2                  // Field $SwitchMap$Color:[I42: getstatic     #7                  // Field Color.YELLOW:LColor;45: invokevirtual #4                  // Method Color.ordinal:()I48: iconst_349: iastore50: goto          5453: astore_054: return

MySwitchTest$1最重要的部分就是静态块static{},指令0~6,调用生成一个int型数组,数组长度和Color.values()的长度一致,并将数组赋值给$SwitchMap$Color字段。指令9~20,调用第一个case的值RED的ordinal(),返回值0,以0为下标,找到数组$SwitchMap$Color的下标为0的元素,赋值为1。指令23,异常处理代码,暂时忽略,后面专门讲解。指令24~35,逻辑与指令9~20相同,为数组$SwitchMap$Color下标为1的元素,赋值为2。指令38,异常处理代码,暂时忽略,后面专门讲解。指令39~50,逻辑与指令9~20相同,为数组$SwitchMap$Color下标为2的元素,赋值为3。指令53,异常处理代码,暂时忽略,后面专门讲解。指令54,return,方法结束。至此,MySwitchTest$1的值为{1, 2, 3}

5.了解了MySwitchTest$1的字节码逻辑之后,我们来看MySwitchTest的chooseColor()方法的字节码。

 public int chooseColor(Color);descriptor: (LColor;)Iflags: ACC_PUBLICCode:stack=2, locals=2, args_size=20: getstatic     #2                  // Field MySwitchTest$1.$SwitchMap$Color:[I3: aload_14: invokevirtual #3                  // Method Color.ordinal:()I7: iaload8: tableswitch   { // 1 to 31: 362: 383: 41default: 44}36: iconst_537: ireturn38: bipush        640: ireturn41: bipush        743: ireturn44: bipush        846: ireturn

指令0~7,获取MySwitchTest$1的字段,由上一节的分析可知该字段值为{1, 2, 3},调用参数Color的ordinal()方法,并以ordinal()方法的返回值为下标,获取MySwitchTest$1.$SwitchMap$Color的元素。由此可知,当参数为RED时,获取的元素为1,当参数BLUE时,获取到的元素为2,当参数YELLOW时,获取到的元素为3,这与上一节根据RED、BLUE、YELLOW元素生成MySwitchTest$1.$SwitchMap$Color的值的逻辑相同。指令8,根据switch生成的指令tableswitch,但是仔细观察case的值并不是RED,BLUE,YELLOW了,而是变成了1,2,3,这也和上一节根据RED、BLUE、YELLOW元素生成MySwitchTest$1.$SwitchMap$Color的值的逻辑相同。指令36~46,根据不同的case值返回不同的结果。

6.总结:由第4节的分析和第5节的分析,可知当switch的变量为Enum时,编译器会生成一个内部类并在该内部类存储一个整型数组,在数组的下标为Enum.ordinal()的位置处存放元素Enum.ordinal()+1。在switch匹配时,也会以枚举变量的ordinal()值为下表,从内部类数组中获取元素作为匹配值,而且switch的case值也是内部类数组值相同。另外,如果想了解switch的字节码,可以参考字节码层面理解Switch,如果想了解Enum的字节码,可以参考字节码层面理解java枚举Enum。

字节码层面理解枚举类Enum在switch中的使用相关推荐

  1. java switch枚举类,使用枚举类enum代替switch

    使用枚举类enum代替switch 使用枚举类,能大大减少switch的代码量,提高代码阅读性 public enum TestEnum { stu1("小明", "一班 ...

  2. catch后面的代码会执行吗_字节码层面理解try、catch、finally

    面试中经常有关于try.catch.finally相关的问题,今天从字节码层面了解他们的运行流程. 简单代码 直接上测试简单代码,如下图: 这里是一个简单的测试代码,代码中有三个异常和一个finall ...

  3. java枚举类Enum入门理解

    目录 枚举的定义 JDK5.0之前只能自定义枚举类 自定义枚举类的理解: JDK5.0之后enum关键字定义枚举类 区别于自定义枚举类 enum的父类Enum的常用方法 toString方法和valu ...

  4. java枚举类与成员变量的关系_深入理解枚举类

    深入理解枚举 最近刚学习完JVM相关知识,想到枚举既然这么异类,那就从字节码角度来分析一下它.有关枚举的讲解,很多博客已经很详细了,这里我们就从字节码的角度重新来认识一下它. 枚举类是一种特殊的类,它 ...

  5. 【Java 枚举 集合】枚举类Enum、映射EnumMap、集EnumSet

    枚举Enum.映射EnumMap.集EnumSet 一.枚举Enum 1.概述 2.介绍 ① valueOf ② values 3.分析 ※ 模仿一个枚举类 二.枚举映射 EnumMap 1.概述 2 ...

  6. java arraylist枚举器遍历_Java基础(七)泛型数组列表ArrayList与枚举类Enum

    一.泛型数组列表ArrayList 1.在Java中,ArrayList类可以解决运行时动态更改数组的问题.ArrayList使用起来有点像数组,但是在添加或删除元素时,具有自动调节数组容量的功能,而 ...

  7. Kotlin 枚举类 enum

    枚举类就是一组命名的常数,是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内. 枚举类的最基本的用法是实现类型安全的枚举: enum class Direction {NORTH, SOUT ...

  8. java枚举类型数组_Java基础(七)泛型数组列表ArrayList与枚举类Enum

    一.泛型数组列表ArrayList 1.在Java中,ArrayList类可以解决运行时动态更改数组的问题.ArrayList使用起来有点像数组,但是在添加或删除元素时,具有自动调节数组容量的功能,而 ...

  9. java switch enum对象_Java枚举类(enum)5种常见用法和3种内部方法,详细附代码

    文章目录 Java枚举类(enum)重点用法和内部方法,附代码 一.Java 枚举类(enum) 详解5种常见的用法 1.常量 2.支持switch 3.向枚举中添加新方法 4.覆盖枚举的方法 5.实 ...

  10. 从字节码层面,解析 Java 布尔型的实现原理

    最近在系统回顾学习 Java 虚拟机方面的知识,其中想到一个很有意思的问题:布尔型在虚拟机中到底是什么类型? 要想解答这个问题,我们看 JDK 的源码是无法解决源码的,我们必须深入到 class 文件 ...

最新文章

  1. 难以想象SpringBoot中的条件注解底层居然是这样实现的
  2. go reflect的用法
  3. 基于JAVA+SpringMVC+Mybatis+MYSQL的健身管理系统
  4. 苹果电脑删除下载的更新文件_解决电脑管家病毒库更新误删除T6文件方法
  5. web前端课程设计源码大全(HTML+CSS+JS)
  6. 利用C#实现的外挂式甲骨文拼音输入法
  7. Windows 8.1 安装Ruby on Rails手记
  8. 离职通知邮件主题写什么好_辞职信邮件主题
  9. 【Latex】Latex小白入门(4)——Latex中特殊符号的输入
  10. 2020 CSP - J初赛 题解
  11. Super NES Programming/SNES Specs
  12. openstack-ocata版本nova MQ(rpc)接收端(server)浅析
  13. 超越美国!中国AI初创企业融资额全球第一 人脸识别最受热捧【附报告全文】
  14. Java截取String字符串的几种方法
  15. 幂律成因的推导过程以及尺度不变性
  16. 案例:谷歌人工智能算法Dropout申请专利
  17. Linux九阴真经之大伏魔拳(zabbix 监控)
  18. mysql“Access denied for user 'root'@'IP地址'
  19. Python深度学习_猫狗大战运行常见问题
  20. expect使用技巧

热门文章

  1. 【RL】算法简介与实现
  2. python判断谁做了好事
  3. CROSS APPLY和OUTER APPLY的区别
  4. TP6.0 一对一模型关联 hasOne
  5. mysql 删除自定义函数_MySQL学习——操作自定义函数
  6. 动态域名解析ipv6 群辉dnspod_群晖设置ipv6动态域名
  7. 如何将计算机网络作为热点,怎么把笔记本作为WIFI热点供其他电脑使用无线网...
  8. YOLO V5 实现课堂行为检测
  9. Java笔记——Java 实现金额小写转大写
  10. 【论文笔记-5】Spatial Transformer Networks(STN)