引言
程序流程离不开条件控制,为了支持条件跳转,虚拟机提供了大量字节码指令,大体上可以分为1)比较指令、2)条件跳转指令、3)比较条件转指令、4)多条件分支跳转指令、5)无条件跳转指令等。数值类型的数据,才可以谈大小!(byte\short\char\int;long\float\double)boolean、引用数据类型不能比较大小。
条件跳转指令
条件跳转指令通常和比较指令结合使用。在条件跳转指令执行前,一般可以先用比较指令进行栈顶元素的准备,然后进行条件跳转。条件跳转指令有:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull。这些指令都接收两个字节的操作数,用于计算跳转的位置(16位符号整数作为当前位置的offset)。它们的统一含义为:弹出栈顶元素,测试它是否满足某一条件,如果满足条件,则跳转到给定位置。
注意
与前面运算规则一致:
对于boolean、byte、char、short类型的条件分支比较操作,都是使用int类型的比较指令完成对于long、float、double类型的条件分支比较操作,则会先执行相应类型的比较运算指令,运算指令会返回一个整型值到操作数栈中,随后再执行int类型的条件分支比较操作来完成整个分支跳转 由于各类型的比较最终都会转为int类型的比较操作,所以Java虚拟机提供的int类型的条件分支指令是最为丰富和强
代码举例
左边/底部更大压栈进入1
public void compare1() {
int a
= 0;
if (a
== 0) {
a
= 10;
} else {
a
= 20;
}
}
public boolean compareNul1(String str
){
if(str
==null
){
return true;
}else {
return false;
}
}
public void compare2() {
float f1
= 9;
float f2
= 10;
System
.out
.println(f1
< f2
);
}
比较条件跳转指令
比较条件跳转指令类似于比较指令和条件跳转指令的结合体,它将比较和跳转两个步骤合二为一。
这类指令有:if_icmpeq、if_icmpne、if_icmplt、if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne。其中指令助记符加上“if_”后,以字符“i”开头的指令针对int型整数操作(也包括short和byte类型),以字符“a”开头的指令表示对象引用的比较。
这些指令都接收两个字节的操作数作为参数,用于计算跳转的位置。同时在执行指令时,栈顶需要准备两个元素进行比较。指令执行完成后,栈顶的这两个元素被清空,且没有任何数据入栈。如果预设条件成立,则执行跳转,否则,继续执行下条语句。
代码举例
public void ifCompare1() {
int i
= 10;
int j
= 20;
System
.out
.println(i
> j
);
}
public void ifCompare2() {
short s1
= 9;
byte b1
= 10;
System
.out
.println(s1
> b1
);
}
public void ifCompare3() {
Object obj1
= new Object();
Object obj2
= new Object();
System
.out
.println(obj1
== obj2
);
System
.out
.println(obj1
!= obj2
);
}
多条件分支跳转
多条件分支跳转指令是专为switch-case语句设计的,主要有tableswitch和lookupswitch。从助记符上看,两者都是switch语句的实现,它们的区别:
tableswitch要求多个条件分支值是连续的,它内部只存放起始值和终止值,以及若干个跳转偏移量,通过给定的操作数index,可以立即定位到跳转偏移量位置,因此效率比较高指令lookupswitch内部存放着各个离散的case-offset对,每次执行都要搜索全部的case-offset对,找到匹配的case值,并根据对应的offset计算跳转地址,因此效率较低。 指令tableswitch的示意图如下图所示。由于tableswitch的case值是连续的,因此只需要记录最低值和最高值,以及每一项对应的offset偏移量,根据给定的index值通过简单的计算即可直接定位到offset。 指令lookupswitch处理的是离散的case值,但是出于效率考虑,将case-offset对按照case值大小排序,给定index时,需要查找与index相等的case,获得其offset,如果找不到则跳转到default。指令lookupswitch如下图所示。
代码举例
public void swtichl(int select
) {
int num
;
switch (select
) {
case 1:
num
= 10;
break;
case 2:
num
= 20;
case 3:
num
= 30;
break;
default:
num
= 40;
}
System
.out
.println(num
);
}
public void swtich2(int select
) {
int num
;
switch (select
) {
case 100:
num
= 10;
break;
case 500:
num
= 20;
break;
case 200:
num
= 30;
break;
default:
num
= 40;
}
}
public void swtich3(String season
) {
switch (season
) {
case "SPRING":
break;
case "SUMMER":
break;
case "AUTUMN":
break;
case "WINTER":
break;
}
}
无条件跳转圈
目前主要的无条件跳转指令为goto。指令goto接收两个字节的操作数,共同组成一个带符号的整数,用于指定指令的偏移量,指令执行的目的就是跳转到偏移量给定的位置处。如果指令偏移量太大,超过双字节的带符号整数的范围,则可以使用指令goto_w,它和goto有相同的作用,但是它接收4个字节的操作数,可以表示更大的地址范围。指令jsr、jsr_w、ret虽然也是无条件跳转的,但主要用于try-finally语句,且已经被虚拟机逐渐废弃,故不在这里介绍这两个指令。
代码举例
public void whileInt() {
int i
= 0;while (i
< 100) {
String s
= "atguigu.com";
i
++;
}
}