英文原文链接,译文链接,原文作者:James Bloom,译者:有孚

从Java7开始,switch语句增加了对String类型的支持。不过字节码中的switch指令还是只支持int类型,并没有增加对其它类型的支持。事实上switch语句对String的支持是分成两个步骤来完成的。首先,将每个case语句里的值的hashCode和操作数栈顶的值(译注:也就是switch里面的那个值,这个值会先压入栈顶)进行比较。这个可以通过lookupswitch或者是tableswitch指令来完成。结果会路由到某个分支上,然后调用String.equlals来判断是否确实匹配。最后根据equals返回的结果,再用一个tableswitch指令来路由到具体的case分支上去执行。

public int simpleSwitch(String stringOne) {

switch (stringOne) {

case "a":

return 0;

case "b":

return 2;

case "c":

return 3;

default:

return 4;

}

}

这个字符串的switch语句会生成下面的字节码:

0: aload_1

1: astore_2

2: iconst_m1

3: istore_3

4: aload_2

5: invokevirtual #2 // Method java/lang/String.hashCode:()I

8: tableswitch {

default: 75

min: 97

max: 99

97: 36

98: 50

99: 64

}

36: aload_2

37: ldc #3 // String a

39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

42: ifeq 75

45: iconst_0

46: istore_3

47: goto 75

50: aload_2

51: ldc #5 // String b

53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

56: ifeq 75

59: iconst_1

60: istore_3

61: goto 75

64: aload_2

65: ldc #6 // String c

67: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

70: ifeq 75

73: iconst_2

74: istore_3

75: iload_3

76: tableswitch {

default: 110

min: 0

max: 2

0: 104

1: 106

2: 108

}

104: iconst_0

105: ireturn

106: iconst_2

107: ireturn

108: iconst_3

109: ireturn

110: iconst_4

111: ireturn

这段字节码所在的class文件里面,会包含如下的一个常量池。关于常量池可以看下JVM内部细节中的_运行时常量池_一节。

Constant pool:

#2 = Methodref #25.#26 // java/lang/String.hashCode:()I

#3 = String #27 // a

#4 = Methodref #25.#28 // java/lang/String.equals:(Ljava/lang/Object;)Z

#5 = String #29 // b

#6 = String #30 // c

#25 = Class #33 // java/lang/String

#26 = NameAndType #34:#35 // hashCode:()I

#27 = Utf8 a

#28 = NameAndType #36:#37 // equals:(Ljava/lang/Object;)Z

#29 = Utf8 b

#30 = Utf8 c

#33 = Utf8 java/lang/String

#34 = Utf8 hashCode

#35 = Utf8 ()I

#36 = Utf8 equals

#37 = Utf8 (Ljava/lang/Object;)Z

注意,在执行这个switch语句的时候,用到了两个tableswitch指令,同时还有数个invokevirtual指令,这个是用来调用String.equals()方法的。在下一篇文章中关于方法调用的那节,会详细介绍到这个invokevirtual指令。下图演示了输入为”b”的情况下,这个swith语句是如何执行的。

如果有几个分支的hashcode是一样的话,比如说“FB”和”Ea”,它们的hashCode都是28,得简单的调整下equals方法的处理流程来进行处理。在下面的这个例子中,34行处的字节码ifeg 42会跳转到另一个String.equals方法调用,而不是像前面那样执行lookupswitch指令,因为前面的那个例子中hashCode没有冲突。(译注:这里一般容易弄混淆,认为ifeq是字符串相等,为什么要跳到下一处继续比较字符串?其实ifeq是判断栈顶元素是否和0相等,而栈顶的值就是String.equals的返回值,而true,也就是相等,返回的是1,false返回的是0,因此ifeq为真的时候表明返回的是false,这会儿就应该继续进行下一个字符串的比较)

public int simpleSwitch(String stringOne) {

switch (stringOne) {

case "FB":

return 0;

case "Ea":

return 2;

default:

return 4;

}

}

这段代码会生成下面的字节码:

0: aload_1

1: astore_2

2: iconst_m1

3: istore_3

4: aload_2

5: invokevirtual #2 // Method java/lang/String.hashCode:()I

8: lookupswitch {

default: 53

count: 1

2236: 28

}

28: aload_2

29: ldc #3 // String Ea

31: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

34: ifeq 42

37: iconst_1

38: istore_3

39: goto 53

42: aload_2

43: ldc #5 // String FB

45: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z

48: ifeq 53

51: iconst_0

52: istore_3

53: iload_3

54: lookupswitch {

default: 84

count: 2

0: 80

1: 82

}

80: iconst_0

81: ireturn

82: iconst_2

83: ireturn

84: iconst_4

85: ireturn

###循环语句

if-else和switch这些条件流程控制语句都是先通过一条指令比较两个值,然后跳转到某个分支去执行。

for循环和while循环这些语句也类似,只不过它们通常都包含一个goto指令,使得字节码能够循环执行。do-while循环则不需要goto指令,因为它们的条件判断指令是放在循环体的最后来执行。

有一些操作码能在单条指令内完成整数或者引用的比较,然后根据结果跳转到某个分支继续执行。而比较double,long,float这些类型则需要两条指令。首先会将两个值进行比较,然后根据结果把1,-1,0压入操作数栈中。然后再根据栈顶的值是大于小于或者等于0,来决定下一步要执行的指令的位置。这些指令在上一篇文章中有详细的介绍。

####while循环

while循环包含条件跳转指令比如if_icmpge 或者if_icmplt(前面有介绍)以及goto指令。如果判断条件不满足的话,会跳转到循环体后的第一条指令继续执行,循环结束(译注:这里判断条件和代码中的正好相反,如代码中是i<2,字节码内是i>=2,从字节码的角度看,是满足条件后循环中止)。循环体的末尾是一条goto指令,它会跳转到循环开始的地方继续执行,直到分支跳转的条件满足才终止。

public void whileLoop() {

int i = 0;

while (i < 2) {

i++;

}

}

编译完后是:

0: iconst_0

1: istore_1

2: iload_1

3: iconst_2

4: if_icmpge 13

7: iinc 1, 1

10: goto 2

13: return

if_icmpge指令会判断局部变量区中的1号位的变量(也就是i,译注:局部变量区从0开始计数,第0位是this)是否大于等于2,如果不是继续执行,如果是的话跳转到13行处,结束循环。goto指令使得循环可以继续执行,直到条件判断为真,这个时候会跳转到紧挨着循环体后边的return指令处。iinc是少数的几条能直接更新局部变量区里的变量的指令之一,它不用把值压到操作数栈里面就能直接进行操作。这里iinc指令把第1个局部变量(译注:第0个是this)自增1。

for循环和while循环在字节码里的格式是一样的。这并不奇怪,因为每个while循环都可以很容易改写成一个for循环。比如上面的while循环就可以改写成下面的for循环,当然了它们输出的字节码也是一样的:

public void forLoop() {

for(int i = 0; i < 2; i++) {

}

}

####do-while循环

do-while循环和for循环,while循环非常类似,除了一点,它是不需要goto指令的,因为条件跳转指令在循环体的末尾,可以用它来跳转回循环体的起始处。

public void doWhileLoop() {

int i = 0;

do {

i++;

} while (i < 2);

}

这会生成如下的字节码:

0: iconst_0

1: istore_1

2: iinc 1, 1

5: iload_1

6: iconst_2

7: if_icmplt 2

10: return

java中字节码_Java字节码浅析(三)相关推荐

  1. Java中动态加载字节码的方法 (持续补充)

    文章目录 Java中动态加载字节码的方法 1.利用 URLClassLoader 加载远程class文件 2.利用 ClassLoader#defineClass 直接加载字节码 2.1 类加载 - ...

  2. java装逼的话_Java 源码装逼技能之让人懵逼的符号

    源码就是符号位 + 二级制数值.符号位是第一位,0 表示正数,1 表示负数. Java 中 byte 类型一字节八位,可以表示 [1111 1111 , 0111 1111],取值 [-127,127 ...

  3. ACM试题 - ASCII码排序 - Java中字符与对应ASCII码的转换

    Java中字符转换对应ASCII码有两种方式: 第一种: char c = 'a'; byte b = (byte)c; // b=97 第二种: char c = 'a'; int b = c; / ...

  4. fileinputstream_从Java中的FileInputStream读取字节

    以下示例显示了如何从Java中的FileInputStream读取字节. import java.io.File;import java.io.FileInputStream;public class ...

  5. java电脑类_计算机类在Java中的设计于实现码

    计算机类在Java中的设计于实现码 问题描述: 一台计算机是由主板.CPU.显卡.声卡等部件组成的,这些部件通过接口可以直接安插在主板的插槽上,也就是说只要将这些部件简单的安插在一起就可以成功组装出一 ...

  6. JAVA中几种循环结构的表示_本文通过实例讲解给大家介绍Java中for、while、do while三种循环语句的区别,具体详情如下所示:第一种:for循环 循环结构for语句的格式...

    本文通过实例讲解给大家介绍Java中for.while.do while三种循环语句的区别,具体详情如下所示: 第一种:for循环 循环结构for语句的格式: for(初始化表达式;条件表达式;循环后 ...

  7. java中字节码_Java字节码浅析(—)

    英文原文链接,译文链接,原文作者:James Bloom,译者:有孚 明白Java代码是如何编译成字节码并在JVM上运行的非常重要,这有助于理解程序运行的时候究竟发生了些什么.理解这点不仅能搞清语言特 ...

  8. java中字节码_Java字节码执行图示

    ★ 查看具体的执行图示,需要先了解一下 java 线程执行的地方,Java 每一个线程执行字节码指令都是在 jvm 虚拟机栈中完成 " 1.JVM 虚拟机栈 每一条 JVM 线程都有自己私有 ...

  9. JAVA中char占用多少字节_Java中char占用几个字节

    https://www.cnblogs.com/louiswong/p/6062417.html https://www.cnblogs.com/fnlingnzb-learner/p/7272348 ...

  10. java中考勤管理_JAVA人事员工考勤管理(含论文)源码

    此系统可以修改,包安装指导,拍下后联系店主.系统品牌: 其他系统 开发语言: .NET 数据库: Mssql 源码参数 源码类别:[毕业设计] 源码类型:B/S 适合人群: 菜鸟进阶 授权类型:商业版 ...

最新文章

  1. mysql 外链接 后面的on_mysql数据库中关于内连接、外链接中on where having的用法。(转载)...
  2. python基础教程第三版怎么样-Python基础教程(第三版)(七)再谈抽象
  3. oce专项认证 oracle_获得Oracle认证对拓展职业前景的影响
  4. Win7+VS2010环境下CEGUI 0.8.4编译过程详解
  5. EOS 智能合约源代码解读 (1)总体说明
  6. boost::hana::accessors用法的测试程序
  7. struts2中dtd失效,代码不提示问题
  8. 爱立信物联网加速器让各行业玩转数据
  9. 使用VS开发C++ 控制台程序或其他项目出现 ‘ LINK : fatal error LNK1104: 无法打开文件“LIBCD.lib” ’ 常规解决办法
  10. SolrCloud详解及搭建
  11. div无法触发blur事件解决的方法
  12. 2020 最烂密码 TOP 200 大曝光,霸榜的仍旧是 123456!
  13. 清风老师数学建模课程——第一讲层次分析法
  14. LOJ 6437 PKUSC2018 PKUSC
  15. 大话西游手游服务器地址修改,大话西游手游怎么转区?大话西游手游转区移民条件一览...
  16. Struck Structured Output Tracking with Kernels阅读笔记
  17. 学习《西方哲学史》摘录
  18. win10如何合并硬盘分区
  19. 科普:QLED和OLED到底有何区别?
  20. 【arc075f】AtCoder Regular Contest 075 F - Mirrored

热门文章

  1. 导出excel表格,前端和后台导出
  2. Laravel5.6 实现后台管理登录(自定义用户表登录)
  3. 简约才是王道? CardView 的使用
  4. scala学习笔记(一)入门初探
  5. 再学 GDI+[83]: TGPImage(3) - 平行四边形变换
  6. 重温WCF之会话Session(九)
  7. (2014年2月7日升级)Ubuntu-14.04-Alpha2-32位简体中文优化封装版
  8. linux上修改ssh密码和mysql密码
  9. 路由协议:RIP/OSPF/BGP—Vecloud微云
  10. CryptoZombies学习笔记——Lesson4