无题
(以下测试均采用 hotspot 虚拟机)讨论两个问题:
finally
以下代码输出什么
import org.junit.Test;
public class NoneTest1 {
public String code(){
String str = "1";
try {
return str;
}finally {
str = "2";
}
}
@Test
public void test(){
System.out.println(code());
}
}
switch
在空间允许的情况下,以下哪个test效率高
public class NoneTest2 {
public int test1(int num){
int res;
switch (num){
case 1:
res = 1;
break;
case 2:
res = 2;
break;
case 3:
res = 3;
break;
default:
res = 0;
}
return res;
}
public int test2(int num){
int res;
switch (num){
case 1:
res = 1;
break;
case 3:
res = 3;
break;
case 2:
res = 2;
break;
default:
res = 0;
}
return res;
}
public int test3(int num){
int res;
switch (num){
case 10:
res = 10;
break;
case 30:
res = 30;
break;
case 40:
res = 40;
break;
default:
res = 0;
}
return res;
}
}
解释
第一个问题
输出的是 1 ,并不会输出 2 ,原因在 oracle(此版本为jdk8) 官方已经给出来了
圈出来的一段的最后一句
# 如果 try 语句中包含return,则编译后的代码执行以下操作
If the try clause executes a return, the compiled code does the following:
# 保存返回值到局部变量表中
1.Saves the return value (if any) in a local variable.
# 执行 finally 代码
2.Executes a jsr to the code for the finally clause.
# 从 finally 返回时,返回保存在局部变量表中的值
3.Upon return from the finally clause, returns the value saved in the local variable.
所以在上方返回的是临时保存在局部变量表中的 str="1"
异常表
当字节码 3—5行出现异常时,直接跳转第 10 行
对应字节码
0 ldc #2 <1> #加载 “1” 到操作数栈
2 astore_1 #保存到局部变量表 1 的位置(0位置是this)
3 aload_1 #加载局部变量表 1 号位到操作数栈
4 astore_2 #保存到 2 号位(对应上面第一句话,保存返回值到局部变量表中)
5 ldc #3 <2> #加载 “2” 到操作数栈
7 astore_1 #保留给 1 号位,此时 1 号位为 “2”,2 号位依旧为1
8 aload_2 #加载 2 号位
9 areturn #返回 2 号位
10 astore_3 #当出现异常时跳转到该位置,保存 异常 到 3 号位
11 ldc #3 <2>
13 astore_1
14 aload_3 #加载 3 号位异常
15 athrow #抛出异常
所以可以看出在返回时做了一次复制操作,需要注意的是,如果返回的是对象,复制的是 对象地址值 ,而不是直接复制一份实例对象。
第二个问题
test1 和 test2 效率相同,都优于 test3
在 oracle switch 章节中,最后一句话说明了,在空间允许的情况下,tableswitch 更优于 lookupswitch
而要被编译成 tableswitch,The tableswitch instruction is used when the cases of the switch can be efficiently represented as indices into a table of target offsets.
当case可以有效的表示为目标偏移表中的索引时,采用tableswitch,简单来说,就是当 case 条件为连续的时候,采用 tableswitch,所以 test1、test2 会被编译成 tableswitch,虽然 test2 case条件并不是连续的,但是在编译的时候会进行排序然后采用 tableswitch。而 test3 采用的是 lookupswitch。
甚至存在如下代码采用的还是 tableswitch
public int test4(int num){
int res;
switch (num){
case 1:
res = 1;
break;
case 3:
res = 3;
break;
case 4:
res = 4;
break;
default:
res = 0;
}
return res;
}