说说java中你不知道switch关键字的奥秘(代码片段)

dengchengchao dengchengchao     2022-12-23     117

关键词:

Switch语法

switch作为Java内置关键字,却在项目中真正使用的比较少。关于switch,还是有那么一些奥秘的。

技术图片

要什么switch,我有if-else

确实,项目中使用switch比较少的一个主要原因就在于它的作用能被if-else代替,况且switch对类型的限制,也阻碍了switch的进一步使用。

先看看switch的语法:

switch(exp)
    case exp1:
        break;
    case exp2:
        break;
    default:
        break;

其中exp的类型限制为:byte ,short , int , char,及其包装类,以及枚举和String(JDK1.7)

为什么要有这些限制?

如果说,switch的功能和if-else的一模一样,那么它存在的意义在哪里?

答案是:switchif-else在设计的时候,是有一定的性能差别的。

看代码:

public class Test 

    public static void switchTest(int a) 

        switch (a) 
            case 1:
                System.out.println("1");
                break;
            case 2:
                System.out.println("2");
                break;
            default:
                System.out.println("3");
                break;
        
    

javap  -c Test.class

结果如下:

  public static void switchTest(int);
    Code:
       0: iload_0
       1: lookupswitch   // 2
                     1: 28
                     2: 39
               default: 50
          
          
    ...

这里面省略一些代码。

可以发现,switch是通过lookupswitch指令实现。那么lookupswitch指令是干嘛的呢?

Java se8文档中的描述可以大概知道:

switch可以被编译为两种指令

  • lookupswitch:当switchcase比较稀疏的时候,使用该指令对int值的case进行一一比较,直至找到对应的case(这里的查找,可以优化为二分查找)
  • tableswitch:当switchcase比较密集的时候,使用case的值作为switch的下标,可以在时间复杂度为O(1)的情况下找到对应的case(可以类比HashMap)

并且文档中还有一段描述:

Java虚拟机的tableswitchlookupswitch指令仅对int数据有效。因为对 bytechar或或short值的操作在内部被提升为int,所以对其switch表达式求值为其中一个类型进行编译,就好像它被计算为要键入一样int。如果 chooseNear方法是使用type编写的,则使用类型时 short将生成相同的Java虚拟机指令int。其他数字类型必须缩小到类型int 以便在a中使用switch

现在,我们应该能够明白,为什么switch关键字会有类型限制了,因为 switch所被翻译的关键字是被限制为int类型的,至于为什么是int,我猜应该是基于性能和实现的复杂度的考量吧。

int之外的类型

我们明白了byte,shor,char,int能被作为switch类型后,再看看枚举和String

public static void switchTest(String a) 

        switch (a) 
            case "1":
                System.out.println("1");
                break;
            case "2":
                System.out.println("2");
                break;
            default:
                System.out.println("3");
                break;
        
    

编译生成Test.class。拖入IDEA进行反编译得到如下代码:

   public static void switchTest(String a) 
        byte var2 = -1;
        switch(a.hashCode()) 
        case 49:
            if (a.equals("1")) 
                var2 = 0;
            
            break;
        case 50:
            if (a.equals("2")) 
                var2 = 1;
            
        

        switch(var2) 
        case 0:
            System.out.println("1");
            break;
        case 1:
            System.out.println("2");
            break;
        default:
            System.out.println("3");
        

    

可以看见,JDK7 所支持的String类型是通过获取String的hashCode来进行选择的,也就是本质上还是int.为什么String可以这样干?这取决于String是一个不变类。

为了防止hash碰撞,代码更加保险的进行了equals判断。

再来看看Enum

public static void switchTest(Fruit a) 
    switch (a) 
        case Orange:
            System.out.println("Orange");
            break;
        case Apple:
            System.out.println("Apple");
            break;
        default:
            System.out.println("Banana");
            break;
    

编译生成Test.class。拖入IDEA进行反编译得到如下代码:

    public static void switchTest(Fruit a) 
        switch(1.$SwitchMap$com$dengchengchao$Fruit[a.ordinal()]) 
        case 1:
            System.out.println("Orange");
            break;
        case 2:
            System.out.println("Apple");
            break;
        default:
            System.out.println("Banana");
        

    

可以看到,枚举支持switch更加简单,直接通过枚举的顺序即可作为相关case

总之:

  • switch的设计按道理来说,是比if-else要快的,但是在99.99%的情况下,他们性能差不多,除非case分支量巨大,但是在case分支过多的情况下,一般应该考虑使用多态重构了。
  • switch虽然支持byte,int,short,char,enum,String但是本质上都是int,其他的只是编译器帮你进行了语法糖优化而已。

尊重劳动成果,转载注明出处


如果觉得写得不错,欢迎关注微信公众号:逸游Java ,每天不定时发布一些有关Java进阶的文章,感谢关注
技术图片

「译」foreach循环中你不知道的3件事(代码片段)

前言本文925字,阅读大约需要7分钟。总括:forEach循环中你不知道的3件事。原文地址:3thingsyoudidn’tknowabouttheforEachloopinJS公众号:「前端进阶学习」,回复「666」,获取一揽子前端技术书籍自弃者扶不起,自强者击不倒。正文你... 查看详情

js中你不知道的一些概念知识(代码片段)

DOM元素e的e.getAttribute(propName)和e.propName有什么区别和联系e.getAttribute(),是标准DOM操作文档元素属性的方法,具有通用性可在任意文档上使用,返回元素在源文件中设置的属性e.propName通常是在HTML文档中访问特定元素的... 查看详情

你不知道的javascript(上卷)小结

...的小缺点,一是,有些东西已经和实际不一样了,比如说关键字module,现在已经和import合并了,之类的,这个跟内容质量关系不大,主要是技术发展总会有变更;二是,有些行文内容,比如列出1、2点之类的,感觉彼此之间 查看详情

php代码审计中你不知道的牛叉技术点

一、前言php代码审计如字面意思,对php源代码进行审查,理解代码的逻辑,发现其中的安全漏洞。如审计代码中是否存在sql注入,则检查代码中sql语句到数据库的传输和调用过程。入门php代码审计实际并无什么门槛要求,只需要... 查看详情

java中你所不知道的cas操作

...并重试。2.CAS操作和synchronized有什么区别呢   synchronized关键字采用悲观锁技术,线程独享锁,其他线程 查看详情

asp.netcore中间件应用实践中你不知道的那些事(代码片段)

一、概述这篇文章主要分享Endpoint终结点路由的中间件的应用场景及实践案例,不讲述其工作原理,如果需要了解工作原理的同学,可以点击查看以下两篇解读文章:Asp.NetCoreEndPoint终结点路由工作原理解读ASP.NETCORE管道模型及中... 查看详情

javascript中你不知道的5个json-使用技巧(代码片段)

开发中,经常使用 JSON.stringify(object) 来序列化对象,但除了第一个参数外,还有其它参数可用...格式化//默认的JSON.stringify(object)出来数据是一行字符串constuser=name:'JackieDYH',age:30,isAdmin:true,friends:['小可爱... 查看详情

说说关于servlet你不知道的知识(代码片段)

    什么是Servlet?    JavaWeb技术是当今主流的互联网Web应用技术之一,而Servlet是JavaWeb技术的核心基础。那么什么是Servlet,什么是Servlet容器呢?    最简单的介绍,Servlet是Sun公司提供的一门用于开发动... 查看详情

你不知道的javascript笔记

this和对象原型this是一个很特别的关键字,被自动定义在所有函数的作用域中//foo.count是0,字面理解是错误的    functionfoo(num){        console.log("foo:"+num);    &n 查看详情

你不知道的javascript笔记

... 区分函数声明和函数表达式最简单的方法是看function关键字出现的位置,如果function是声明中的第一个词,那么是函数声明,否则是函数表达式。(functionfoo(){} 查看详情

云栖大会中你不可以错过的技术盛宴!

2017阿里云杭州云栖大会ClouderLab探秘带上电脑去云栖,与专家面对面顺便拿认证!报名9月30日截止! 杭州云栖大会推出的4场ClouderLab开放实验室,为企业级技术人员提供了一个动手实操的实验平台,帮助他们清晰理解云计算... 查看详情

java——final关键字

前言Java中的关键字final的含义通常为“这是无法改变的”。下面将介绍final用于修饰数据、方法和类的这三种情况。final数据许多编程语言都有某种方法,来向告诉编译器这一块数据是不变的。有时候数据的恒定不变会很有用,... 查看详情

java——switch选择结构

...块的表示。switch语法格式: switch选择结构用到了四个关键字 :》switch:表示“开关”这个开关就是switch关键字后面小括号里的表达式的值,jdk1.7后, 查看详情

《你不知道的js(中卷①)》语法(代码片段)

...grammar)与词法(syntax)不同。后者强调语言的运算符、关键字等。而语法定义了此法规则是如何构成可运行的程序代码的。一)、语句和表达式:语句(statement)与表达式(expression),举例说明:vara=3*6;a=3*6是一个赋值表达式... 查看详情

你不知道的javascript事件(代码片段)

...提出了完全相反的的概念:事件冒泡和事件捕获。下面就说说这两种事件流:事件冒泡事件冒泡,就是说时间开始时由具体的元素接受,然后逐级向上传播到较为不具体的节点。看看下面的图就比较清楚了:比如说在图中的<di... 查看详情

java8中你可能不知道的一些地方之stream实战(代码片段)

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。Optional对象构建&值获取方法实例代码如下Optional<Str... 查看详情

java8中你可能不知道的一些地方之optional实战(代码片段)

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。Optional对象构建&值获取方法实例代码如下Optional<Str... 查看详情

java8中你可能不知道的一些地方之lambda(代码片段)

Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。很多语言(Groovy、Scala等)从设计之初就支持Lambda表达式。但是java中使用的是匿名内部类代替。最后借助强大的社区力量... 查看详情