`
freish
  • 浏览: 82949 次
  • 性别: Icon_minigender_1
  • 来自: 摄影帝国
社区版块
存档分类
最新评论

jdk1.7 String switch的实现

 
阅读更多

 

对于int的switch,jvm是用tableswitch和lookupswitch来实现的,jdk1.7 switch增加了对string的支持,那么底层是如何实现的呢?是否增加了新的指令或是否给某些指令增加了新的含义?

 

 

看这样一个程序:

 

public class Test {   
	public static void main(String[] args) {  
		String name = "b";
		int value = 0;
		switch(name) {
			case "a":
				value = 1;
				break;
			case "b":
				value = 2;
				break;
			case "c":
				value = 3;
				break;
			case "d":
				value = 4;
				break;
			case "e":
				value = 5;
				break;
			default:
				value = 6;
		}
		System.out.println(value);
	}  
}

 

javap -c Test得出的结果为:

 

 

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String b
       2: astore_1					//将"b"赋值给name
       3: iconst_0					//将0入栈
       4: istore_2					//将0赋值给value
       5: aload_1					//将name(即"b")入栈
       6: astore_3					//将name(即"b")赋值给一个编译器生成的变量,记为tmpName(tmpName此时也是"b")
       7: iconst_m1					//将-1入栈
       8: istore        4			//将-1赋值给一个编译器生成的变量,记为m1
      10: aload_3					//将tmpName(即"b")入栈
      11: invokevirtual #3                  // Method java/lang/String.hashCode:()I		调用tmpName的hashCode方法("b".hashCode(),得结果98)
      14: tableswitch   { // 97 to 101		//根据hashCode的值到不同的分支
                    97: 48
                    98: 63					//这里走到这个分支,跳转到63
                    99: 78
                   100: 93
                   101: 108
               default: 120
          }
      48: aload_3
      49: ldc           #4                  // String a
      51: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      54: ifeq          120
      57: iconst_0
      58: istore        4
      60: goto          120
      63: aload_3							//从14跳转到了这里,将tmpName(即"b")入栈
      64: ldc           #2                  // String b		将"b"入栈
      66: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z	
											//比较tmpName和"b",如果相等,将1入栈,不等将0入栈.这里是相等的,入栈的为1
      69: ifeq          120					//从栈顶取出比较结果,如果等于0,就跳到120,如果不等于0就继续下面的指令,这里显然不等于0
      72: iconst_1							//将1入栈
      73: istore        4					//将1存储到m1中
      75: goto          120					//跳到120
      78: aload_3
      79: ldc           #6                  // String c
      81: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      84: ifeq          120
      87: iconst_2
      88: istore        4
      90: goto          120
      93: aload_3
      94: ldc           #7                  // String d
      96: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      99: ifeq          120
     102: iconst_3
     103: istore        4
     105: goto          120
     108: aload_3
     109: ldc           #8                  // String e
     111: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
     114: ifeq          120
     117: iconst_4
     118: istore        4
     120: iload         4					//将m1(即73行存进去的1)的值入栈
     122: tableswitch   { // 0 to 4			
                     0: 156
                     1: 161					//这里走1这个分支,跳到161
                     2: 166
                     3: 171
                     4: 176
               default: 181
          }
     156: iconst_1
     157: istore_2
     158: goto          184
     161: iconst_2					//将2入栈
     162: istore_2					//将2存储到value
     163: goto          184			//跳转到184进行打印输出
     166: iconst_3
     167: istore_2
     168: goto          184
     171: iconst_4
     172: istore_2
     173: goto          184
     176: iconst_5
     177: istore_2
     178: goto          184
     181: bipush        6
     183: istore_2
     184: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     187: iload_2
     188: invokevirtual #10                 // Method java/io/PrintStream.println:(I)V
     191: return
}
 

 

在这一堆指令中我们发现,jdk1.7并没有新指令来处理string switch,还是继续用lookupswitch和tableswitch两个指令来处理的,也没有扩展这两个指令,它们还是只能处理int。

 

 

在11行,我们看到调用了需要switch的string的hashCode方法,并对该hashCode进行switch,并跳转到相应的处理指令。跳到新的指令处我们发现这里(63行)将待switch的变量(name)与case中的值("b")equals了一下。这样做是为了避免不同的string有相同的hashCode,确定equals返回true后,编译器生成了一个处理value的switch,源码里有多少个case编译器生成的tableswitch就有多少个分支,最终会找到相应的处理分支完成string switch的处理。

 

 

接下来,看一个不同string hashCode相等的版本:

 

buzzards与righto的hashCode相等

hierarch与crinolines的hashCode相等

 

这里选择buzzards和righto

 

 

public class Test {   
	public static void main(String[] args) {  
		String name = "buzzards";
		int value = 0;
		switch(name) {
			case "buzzards":
				value = 1;
				break;
			case "righto":
				value = 2;
				break;
			default:
				value = 6;
		}
		System.out.println(value);
	}  
}
 

 

字节码:

 

 public static void main(java.lang.String[]);
   Code:
      0: ldc           #2                  // String buzzards
      2: astore_1
      3: iconst_0
      4: istore_2
      5: aload_1
      6: astore_3
      7: iconst_m1
      8: istore        4
     10: aload_3
     11: invokevirtual #3                  // Method java/lang/String.hashCode:()I
     14: lookupswitch  { // 1
           -931102253: 32
              default: 59
         }
     32: aload_3
     33: ldc           #4                  // String righto
     35: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)
     38: ifeq          47
     41: iconst_1
     42: istore        4
     44: goto          59
     47: aload_3
     48: ldc           #2                  // String buzzards
     50: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)
     53: ifeq          59
     56: iconst_0
     57: istore        4
     59: iload         4
     61: lookupswitch  { // 2
                    0: 88
                    1: 93
              default: 98
         }
     88: iconst_1
     89: istore_2
     90: goto          101
     93: iconst_2
     94: istore_2
     95: goto          101
     98: bipush        6
    100: istore_2
    101: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
    104: iload_2
    105: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
    108: return
 

 

 

这里我们看到两个字符串都跳到32行,首先跟"righto"比较,如果不相等则跳到47行比较buzzards。

 

 

13
8
分享到:
评论
3 楼 wkl17 2014-10-26  
字节码 是如何查看的?用记事本打开都是乱码..
2 楼 NeverGiveUpToChange 2012-04-10  
hao hao hao hao hao
1 楼 wupuyuan 2011-08-31  
多谢LZ来分享,我也仔细分析了下,觉得switch String 不如传统的if else来的效率高。当然在if时要判断更准确一些。欢迎去我空间看看

相关推荐

    jdk1.7 官方正式版64位下载

    JDK1.7新特性介绍 1. 对Java集合(Collections)的增强支持 2. 在Switch中可用String 在JDK7 的正式版本中,你可以在switch的表达式中用String类型 3. 数值可加下划线 下划线字符(_)能够出现在数字字面量的数字...

    jdk1.7 windows 64位 官方下载

    JDK1.7新特性介绍 1. 对Java集合(Collections)的增强支持 2. 在Switch中可用String 3. 数值可加下划线 4. 支持二进制文字 5. 简化了可变参数方法的调用 ....

    MacOS下64位jdk1.7

    jdk1.7 仅包含macos 系统支持的dmg jdk1.7新特性 1 对集合类的语言支持; 2 自动资源管理; 3 改进的通用实例创建类型推断;...5 switch中使用string; 6 二进制字面量; 7 简化可变参数方法调用。

    jdk1.7.zip

    jdk1.7jdk17新特性详解 二进制字面量 在数字字面量使用下划线 switch可以使用string了 实例创建的类型推断 使用Varargs方法使用不可维护的形式参数时改进了编译器警告和错误 try-with-resources 资源的自动管理 捕捉...

    JDK1.7.zip

    自动资源管理;改进的通用实例创建类型推断; 数字字面量下划线支持; switch中使用string; 二进制字面量; 简化可变参数方法调用;

    JDK1.7 32位

    在JDK1.7中,摒弃了Java集合接口的实现类,如:ArrayList、HashSet和HashMap。而是直接采用[]、{}的形式存入对象,采用[]的形式按照索引、键值来获取集合中的对象,如下: List<String> list = ["item"]; // 向List...

    jdk-1.7 64位 官方原版

    jdk1.7新特性 1 对集合类的语言支持; 2 自动资源管理; 3 改进的通用实例创建类型推断; 4 数字字面量下划线支持; 5 switch中使用string; 6 二进制字面量; 7 简化可变参数方法调用

    jdk1.7(jdk-7u51-windows-x64)

    jdk1.7新特性 1 对集合类的语言支持; 2 自动资源管理; 3 改进的通用实例创建类型推断; 4 数字字面量下划线支持; 5 switch中使用string; 6 二进制字面量; 7 简化可变参数方法调用。

    jdk1.7.0_67

    jdk1.7新特性 1 对集合类的语言支持; 2 自动资源管理; 3 改进的通用实例创建类型推断; 4 数字字面量下划线支持; 5 switch中使用string; 6 二进制字面量; 7 简化可变参数方法调用。

    jdk-1.7 64位(jdk-7u79-windows-x64)

    jdk1.7新特性 1 对集合类的语言支持; 2 自动资源管理; 3 改进的通用实例创建类型推断; 4 数字字面量下划线支持; 5 switch中使用string; 6 二进制字面量; 7 简化可变参数方法调用。

    jdk-7-window-64

    jdk1.7的新特性主要内容: 在switch中使用String,在1.7以前只支持byte,short,int,char,enum。使用方式和使用byte,short那些一样,就不举例子了。 try-with-resources,之前使用的很多IO操作都需要手动去关闭流,...

    涵盖了90%以上的面试题

    jdk1.7新特性 jdk1.8新特性 java语言有哪些优点? 同一个.java文件中是否可以有多个main方法 一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 如何在main方法执行前输出”hello world” java程序...

    springmybatis

    mybatis 的开发环境搭建,选择: eclipse j2ee 版本,mysql 5.1 ,jdk 1.7,mybatis3.2.0.jar包。这些软件工具均可以到各自的官方网站上下载。 首先建立一个名字为 MyBaits 的 dynamic web project 1. 现阶段,你可以...

    疯狂JAVA讲义

    9.3.2 String、StringBuffer和StringBuilder类 322 9.3.3 Math类 327 9.3.4 Random类 328 9.3.5 BigDecimal类 330 9.4 处理日期的类 333 9.4.1 Date类 333 9.4.2 Calendar类 334 9.4.3 TimeZone类 337 9.5 ...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    基本信息 作者: 臧萌 ...12.2.4 使用接口仅需一步——实现接口 342 12.2.5 接口——让类集多重类型于一身 344 12.2.6 简化recordTransport()方法 347 12.3 再探接口 349 12.3.1 重温上节中的程序 349...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    基本信息 作者: 臧萌 ...12.2.4 使用接口仅需一步——实现接口 342 12.2.5 接口——让类集多重类型于一身 344 12.2.6 简化recordTransport()方法 347 12.3 再探接口 349 12.3.1 重温上节中的程序 349...

Global site tag (gtag.js) - Google Analytics