`
deepinmind
  • 浏览: 444101 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:40752
社区版块
存档分类
最新评论

Java中字符串switch的实现细节

阅读更多

自从Java允许在switch及case语句中使用字符串以来,许多开发人员都使用了这一特性,不过如果使用整型或者枚举的话会更好。这是JDK7中最受欢迎的特性之一,同样的还有自动资源管理以及多异常捕获。尽管我个人不太喜欢这个特性,因为使用枚举的方式其实更好,但我并不是特别反对使用它。一个原因当然是它很方便,如果程序中已经用到了字符串,这样做的确很顺手,不过我建议在生产环境的代码中使用新特性之前最好了解下它是如何工作的。我第一次听说这个特性的时候,我认为这肯定是通过equals()和hashCode()方法来实现的,我更关心的是Java 7中的字符串的switch是如何实现的。我对这个感兴趣还有一个原因,是我想在面试中问一下这个问题,如果面试中有类似这样的一个问题的话会非常有趣。验证它其实非常简单,你只需用字符串写一段switch的代码,然后反编译一下,看看编译器是如何翻译它们的就可以了。那么还等什么,赶快来看下switch中的字符串是如何工作的吧?

原始代码:

这是一个简单的测试程序,它有一个main方法,里面有一个switch块在操作String变量。程序中用到的字符串参数是运行时传递进来的,你可以从main方法的字符串数组中获取到。有三种模式来启动这个程序,主动模式,被动模式,以及安全模式。对于这些已知的确定值,其实用枚举来实现要更好,但如果你已经决定使用字符串了,你得确保你写的是大写的,不要写成小成或者骆驼式的,这样会出现大小写敏感的问题。你还可以看下这篇教程 ,了解下如何在Java 7的switch表达式中正确地使用字符串。

/** * Java Program to demonstrate how string in switch functionality is implemented in * Java SE 7 release. */ 
public class StringInSwitchCase { 
      public static void main(String[] args) { 
            String mode = args[0]; 
            switch (mode) { 
                  case "ACTIVE": 
                        System.out.println("Application is running on Active mode"); 
                        break; 
                  case "PASSIVE":
                        System.out.println("Application is running on Passive mode"); 
                         break; 
                  case "SAFE": 
                          System.out.println("Application is running on Safe mode"); 
          } 
      } 
}




编译运行这段代码需要安装JDK7才行。随便哪个版本的JDK7都可以。

反编译后的代码:

下面是上述代码使用jdk1.7.0_40编译后再反编译的结果。如果你是Java新手,想知道如何反编译Java类来实现逆向工程,看下这篇文章。JDK的每个版本都会加入越来越多的语法糖,因此对于各个水平的Java开发人员来说,知道 如何反编译Java类是想当重要的。你写的代码和实现执行的差距会越来越大。了解Java类的文件格式以及字节码指令会对你很有帮助 。Java 8最近发布了一个新的特性,叫做lambda表达式,它通过编译器来实现了内部匿名类,你可以反编译下你的类文件来看下编译器都加了些什么。

/** * Reverse Engineered code to show how String in Switch works in Java. */ 
import java.io.PrintStream; 

public class StringInSwitchCase{ 
      public StringInSwitchCase() { } 

      public static void main(string args[]) { 
             String mode = args[0]; 
            String s; switch ((s = mode).hashCode()) { 
                  default: break; 
                  case -74056953: 
                        if (s.equals("PASSIVE")) { 
                                    System.out.println("Application is running on Passive mode"); 
                         } 
                        break; 
                  case 2537357: 
                        if (s.equals("SAFE")) { 
                              System.out.println("Application is running on Safe mode"); 
                         } 
                        break; 
                  case 1925346054: 
                        if (s.equals("ACTIVE")) { 
                              System.out.println("Application is running on Active mode"); 
                         } 
                        break; 
               } 
          } 
}




看到这个代码,你知道原来字符串的switch是通过equals和hashCode()方法来实现的。记住,switch中只能使用整型,比如byte。short,char以及int。还好hashCode()方法返回的是int,而不是long。通过这个很容易记住hashCode返回的是int这个事实,事实上我自己都会经常忘了或者弄混。仔细看下可以发现,进行switch的实际是哈希值,然后通过使用equals方法比较进行安全检查,这个检查是必要的,因为哈希可能会发生碰撞。因此它的性能是不如使用枚举进行switch或者使用纯整数常量,但这也不是很差。因为Java编译器只增加了一个equals方法,如果你比较的是字符串字面量的话会非常快,比如"abc" =="abc"。如果你把hashCode()方法的调用也考虑进来了,那么还会再多一次的调用开销,因为字符串一旦创建了,它就会把哈希值缓存起来,这个可以看下我自己比较喜欢的一篇文章 为什么Java中的字符串是不可变的。因此如果这个siwtch语句是用在一个循环里的,比如逐项处理某个值,或者游戏引擎循环地渲染屏幕,这里hashCode()方法的调用开销其实不会很大。不管怎样,我仍然认为使用字符串的switch来代表几个固定的值不是一个最佳实践,Java里的枚举的存在是有它的原因的,每个Java开发人员都应该使用它。

这就是Java 7如何实现的字符串switch。正如我所料,它使用了hashCode()来进行switch,然后通过equals方法进行验证。这其实只是一个语法糖,而不是什么内建的本地功能。选择权在你,我个人来说不是很喜欢在switch语句中使用字符串,因为它使得代码更脆弱,容易出现大小写敏感的问题,而且编译器又没有做输入校验 。事实上对于性能关键的代码,以前的整型常量和枚举的写法是我的最爱,在这里可读性和代码质量都更重要。事实上,99。99%的情况下,枚举都比使用字符串的switch或者整型要好,这也是它们存在于Java语言中的实际意义 。这个特性就是为了改变这种不良的编码实践而生的,我很难找到什么情况下非要针对一组输入值在switch分支中使用字符串,如果你有一个令人信服的使用字符串switch的原因,请告诉我,我或者会改变我现在的想法。

译注:更深入的话可以了解下Java在字节码层面是如何实现的,可参考这篇文章

原创文章转载请注明出处:http://it.deepinmind.com

英文原文链接



3
0
分享到:
评论

相关推荐

    AIC的Java课程1-6章

    第5 版 清华大学出版社  “SCJP学习指南” 人民邮电出版社  “Java 编程思想” 第3版 机械工业出版社  教学内容和要求 知识点 重要程度 使用频度 难度 Java 入门 高 中 易 变量和运算符 高 ...

    JAVA基础课程讲义

    字符串(java.lang.String类)的使用 90 字符串相等的判断 92 思考作业 93 上机作业 94 第四章 异常机制 95 导引问题 95 异常(Exception)的概念 96 异常分类 96 Error 97 Error和Exception的区别 97 Exception 97 ...

    JAVA面试题最全集

    写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入bbbwlirbbb,输出bbbhhtccc。 3.数据类型之间的转换 如何将数值型字符转换为数字(Integer,Double) 如何将数字...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

    4.4.3 字符串编码 115 4.5 查询java api文件 117 4.6 重点复习 119 4.7 课后练习 120 chapter5 对象封装 125 5.1 何谓封装 126 5.1.1 封装对象初始流程 126 5.1.2 封装对象操作流程 128 5.1.3 封装...

    java 面试题 总结

    JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变...

    超级有影响力霸气的Java面试题大全文档

     JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要...

    突破程序员基本功的16课.part2

    第3课 常见Java集合的实现细节 3.1 Set和Map 3.1.1 Set和Map的关系 3.1.2 HashMap和HashSet 3.1.3 TreeMap和TreeSet 3.2 Map和List 3.2.1 Map的values()方法 3.2.2 Map和List的关系 3.3 ArrayList和...

    asp.net知识库

    .NET 2.0中的字符串比较 小试ASP.NET 2.0的兼容性 为 asp.net 2.0 的菜单控件增加 target 属性 ASP.NET 2.0 的内部变化 常见的 ASP.NET 2.0 转换问题和解决方案 Asp.Net2.0无刷新客户端回调 体验.net 2.0 的优雅(1...

    Tcl_TK编程权威指南pdf

    第4章 tcl中的字符串处理 string命令 append命令 format命令 scan命令 binary命令 相关章节 第5章 tcl列表 tcl列表 构建列表 获取列表元素 修改列表 搜索列表 对列表进行排序 split命令 join命令 ...

Global site tag (gtag.js) - Google Analytics