原来你是这样的switch~

在 switch-case 语句的条件判断中,或许使用整形或者枚举更好,但由于种种历史原因,项目中已大量使用字符串的情况下,只得硬着皮头往前冲了。对 switch 支持 String 的实现原理感兴趣的原因,是在跟组员探讨线上的一个空指针异常来的,以前根本没意识到小小的switch 还有这样的玩法。

要对 switch 的原理追根溯源,我们来写一段简单的 switch 代码,反编译来看看字节码层是什么效果。

public class Testk {
    public static void main(String[] args) {
        String key = null;
        switch (key) {
            case "java":
                System.out.println("caught java");
                break;
            case "android":
                System.out.println("caught android");
                break;
        }
    }
}

这里我们定义参数key,根据 key 值跳转不同的 case 逻辑。

正常情况下,使用 “javac <.java> ”生成.class 文件,使用“javap -verbose <.class>”即可得到字节码,但由于javap得到的字节码结构难以理解,这里我们使用 JD-Gui 工具来查看。

Mac 下安装JD-Gui工具

brew cask install jd-gui

把*.class 文件拖入打开的 JD-Gui窗口,即可得到如下结果:

import java.io.PrintStream;

public class Testk
{
  public static void main(String[] paramArrayOfString)
  {
    Object localObject1 = null;
    Object localObject2 = localObject1;int i = -1;
    switch (((String)localObject2).hashCode())
    {
    case 3254818: 
      if (((String)localObject2).equals("java")) {
        i = 0;
      }
      break;
    case -861391249: 
      if (((String)localObject2).equals("android")) {
        i = 1;
      }
      break;
    }
    switch (i)
    {
    case 0: 
      System.out.println("caught java");
      break;
    case 1: 
      System.out.println("caught android");
    }
  }
}

通过编译后的代码,我们知道 switch 处理字符串是先获取hashCode ->equals()来实现的。

看到这里,我们明白文首的空指针是怎么来的了,编译器针对 switch 的 String 做编译处理时, 需要针对 key 做做非空校验
另外,这里先基于 hashCode()再 equals()方法进行安全检查是有必要的,用来避免 hash 碰撞。

网上很多人都不建议使用字符串,给出的理由多半是String 的大小写使得代码更脆弱。在我看来,代码的脆弱多数是研发人员的代码风格不规范导致。
就拿上面的代码片段来说,改成全局定义变量即可解决大小写敏感问题。

public class Testk {
    private static final String KEY_JAVA=“java”;
    private static final String KEY_ANDROID=“android”;

    public static void main(String[] args) {
        String key = null;
        switch (key) {
            case KEY_JAVA:
                System.out.println("caught java");
                break;
            case KEY_ANDROID:
                System.out.println("caught java");
                break;
        }
    }
}

相关推荐