共计 3417 个字符,预计需要花费 9 分钟才能阅读完成。
一、管制转移指令概述
程序流程离不开条件管制,为了反对条件跳转,虚拟机提供了大量字节码指令,大体上能够分为
- 比拟指令、
- 条件跳转指令、
- 比拟条件跳转指令、
- 多条件分支跳转指令、
- 无条件跳转指令等。
后面咱们也提到过有比拟指令,指的是比拟两个栈顶元素的大小,并将比拟后果入栈
比拟指令有: dcmpg,dcmpl、fcmpg、fcmpl、lcmp
与后面解说的指令相似,首字符 d 示意 double 类型,f 示意 float,l 示意 long
对于 double 和 float 类型的数字,因为 NaN 的存在,各有两个版本的比拟指令
以 float 为例有 fcmpg 和 fcmpl 两个指令,区别在于在数字比拟时若遇到 NaN 值处理结果不同
指令 dcmpl 和 dcmpg 也是相似的,依据其命名能够揣测其含意,在此不再赘述
指令 lcmp 针对 long 型整数,因为 long 型整数没有 NaN 值,故无需筹备两套指令
举例比拟
================================
指令 fcmpg 和 fcmpl 都从栈中弹出两个操作数,并将它们做比拟
设栈顶的元素为 v2, 核顶顺位第 2 位的元素为 v1
若 v1=v2 则压入 0
若 v1>v2 则压入 1
若 v1<v2 则压入 1
两个指令的不同之处在于,如果遇到 NaN 值,fcmpg 会压入 1,而 fcmpl 会压入 -1
二、管制转移指令的条件跳转指令解说
条件跳转指令通常和比拟指令联合应用。
在条件跳转指令执行前,个别能够 先用比拟指令进行栈顶元素的筹备,而后进行条件跳转
条件跳转指令有: ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull
这些指令都接管两个字节的操作数,用于计算跳转的地位(16 位符号整数作为以后地位的 offset)
它们的对立含意为:弹出栈顶元素测试它是否满足某一条件,如果满足条件,则跳转到给定地位
具体指令阐明如下图所示:
留神:与后面运算规定统一
对于 boolean、byte、char、short 类型的条件分支比拟操作,都是应用 int 类型的比拟指令实现
对于 long、float、double 类型的条件分支比拟操作,则会先执行相应类型的比拟运算指令,运算指令会返回一个整型值到操作数栈中,随后再执行 int 类型的条件分支比拟操作来实现整个分支跳转
因为比拟类型最终都会转为 int 类型,所以对于 int 类型的条件分支指令是最为丰盛和弱小的。
接下来咱们应用示例代码来领会领会条件跳转指令是怎么样的?
public class IfSwitchGotoTest {
//1. 条件跳转指令
public void compare1(){
int a = 0;
if(a == 0){a = 10;}else{a = 20;}
}
}
接下来咱们编译代码应用插件查看具体的字节码是怎么样的?
接下来咱们剖析一下字节码指令,看看具体做了哪些事件?
接下来咱们再应用示例代码来领会领会条件跳转指令是怎么样的?
public class IfSwitchGotoTest {
//1. 条件跳转指令
public boolean compareNull(String str){if( str == null){return tlue;}else{return false;}
}
}
接下来咱们编译代码应用插件查看具体的字节码是怎么样的?
接下来咱们下一个示例代码来领会不同的条件跳转指令是怎么样的?
public class IfSwitchGotoTest {
// 联合比拟指令
public void compare2() {
float f1 = 9;
f1oat f2 = 10;
system.out.print1n(f1 < f2);
}
}
接下来咱们编译代码应用插件查看具体的字节码是怎么样的?
接下来咱们剖析一下字节码指令,看看具体做了哪些事件?
三、管制转移指令的比拟条件跳转指令解说
比拟条件跳转指令相似于比拟指令和条件跳转指令的结合体,它将比拟和跳转两个步骤合二为一
这类指令有: if_icmpeq、if_icmpne、if_icmpdt、if_icmpgt、if_icmple、if_icmpge、if_acmpeq、if_acmpne
其中指令助记符加上“if_”后,以字符“i”结尾的指令针对 int 型整数操作(也包含 short 和 byte 类型),以字符“a”结尾的指令示意对象援用的比拟。
这些指令都接管两个字节的操作数作为参数,用于计算跳转的地位。
同时在 执行指令时
,栈顶须要 筹备两个元素
进行比拟。指令 执行实现后
,栈顶的这 两个元素被清空,且没有任何数据入栈
。如果预 没条件成立,则执行跳转
,否则,继续执行下一条语句。
接下来咱们再应用示例代码来领会领会比拟条件跳转指令是怎么样的?
public class IfSwitchGotoTest {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 计算跳转地址, 因而效率较低
接下来咱们针对于多条件分支跳转指令的根本测试,请看以下示例代码
//3. 多条件分支跳转
public void swtich1(int select){
int num;
switch(select){
case 1:
num = 10;
break;
case 2:
num = 20;
break;
case3:
num = 30;
break;
default:
num = 40;
}
}
接下来咱们编译该代码,看看 swtich1 办法的字节码是什么状况?
如果咱们的 switch 分支里的 case 并没有 break 完结的话,会是什么状况呢?看看示例代码
public void swtich1(int select){
int num;
switch(select){
case 1:
num = 10;
break;
case 2:
num = 20;
case3:
num = 30;
break;
default:
num = 40;
}
}
接下来咱们编译该代码,看看 swtich1 办法的字节码是什么状况?
接下来咱们应用下一个示例代码来领会多条件分支跳转指令,请看以下示例代码
public void swtich2(int select){
int num;
switch(select){
case 100:
num = 10;
break;
case 500:
num = 20;
break;
case 208:
num = 30;
break;
default:
num = 40;
}
}
接下来咱们编译该代码,看看 swtich2 办法的字节码是什么状况?
咱们晓得在 JDK7 新个性是 String 类型,接下来应用咱们看看 String 类型的 switch 是怎么样
//jdk7 新个性; 引入 string 类型
public void swtich3(String season){switch(season){
case "SPRING" : break;
case "SUMMER" : break;
case "AUTUMN" : break;
case "WINTER" : break;
}
}
接下来咱们编译该代码,看看 swtich2 办法的字节码是什么状况?
五、管制转移指令的无条件跳转指令解说
目前次要的无条件跳转指令为:goto
指令 goto 接管两个字节的操作数,独特组成一个带符号的整数,用于 指定指令的偏移量,指令执行的目标就是跳转到偏移量给定的地位处
如果指令偏移量太大,超过双字节的带符号整数的范畴,则能够应用指令goto_w
,它和 goto 有雷同的作用,然而它接管 4 个字节的操作数, 能够示意更大的地址范畴
指令 jsr、jsr_w、ret 尽管也是无条件跳转的,但次要用于 try-finally 语句,且曾经被虚拟机逐步废除,故不在这里介绍这两个指令