编译期语法糖(二)

编译期语法糖(二)

foreach循环

在JDK5以后,开始引入的一种新的语法糖.支持数组和集合的遍历.

我们先来看数组:

1
2
3
4
5
6
7
8
public class Demo05_1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};// 数组初始化的语法也是语法糖哦
for (int i : arr) {
System.out.println(i);
}
}
}

我们先通过javap命令反编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=6, args_size=1
0: iconst_5
1: newarray int
3: dup
4: iconst_0
5: iconst_1
6: iastore
7: dup
8: iconst_1
9: iconst_2
10: iastore
11: dup
12: iconst_2
13: iconst_3
14: iastore
15: dup
16: iconst_3
17: iconst_4
18: iastore
19: dup
20: iconst_4
21: iconst_5
22: iastore
23: astore_1
24: aload_1
25: astore_2
26: aload_2
27: arraylength
28: istore_3
29: iconst_0
30: istore 4
32: iload 4
34: iload_3
35: if_icmpge 58 // if_icmpge 指令的意思是如果一个值大于或等于另外一个
38: aload_2
39: iload 4
41: iaload
42: istore 5
44: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
47: iload 5
49: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
52: iinc 4, 1
55: goto 32
58: return
LineNumberTable:
...
LocalVariableTable:
...
}

在指令的前 24条,就是新建数组队列的语法糖

转换成对应的代码如下:

1
2
3
4
5
6
int[] arr = new int[5];
arr[0] =1;
arr[1] =2;
arr[2] =3;
arr[3] =4;
arr[4] =5;

然后我们再来分析后面的字节码,可以转换成对应的代码如下:

1
2
3
4
int length = arr.length;
for (int i = 0; i < length; i++) {
System.out.println(arr[i]);
}

可以看出,foreach 也是编译成fori进行遍历


集合进行foreach.

1
2
3
4
5
6
7
8
9
public class Demo05_2 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5);
for (Integer i : list) {
System.out.println(i);
}

}
}

使用javap进行反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=1
0: iconst_5
1: anewarray #2 // class java/lang/Integer
4: dup
5: iconst_0
6: iconst_1
7: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
10: aastore
11: dup
12: iconst_1
13: iconst_2
14: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
17: aastore
18: dup
19: iconst_2
20: iconst_3
21: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
24: aastore
25: dup
26: iconst_3
27: iconst_4
28: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
31: aastore
32: dup
33: iconst_4
34: iconst_5
35: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
38: aastore
39: invokestatic #4 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
42: astore_1
43: aload_1
44: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
49: astore_2
50: aload_2
51: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
56: ifeq 79
59: aload_2
60: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
65: checkcast #2 // class java/lang/Integer
68: astore_3
69: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
72: aload_3
73: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
76: goto 50
79: return
LineNumberTable:
...
LocalVariableTable:
...
}

44: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator

可以看出这里调用了list.iterator 方法

51: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z

56: ifeq 79

这里调用了iterator.hasNext()方法,且如果为false跳转到79行

60: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;

65: checkcast #2 // class java/lang/Integer

这里通过iterator.next()获取元素,且获取的元素类型为Object ,需要调用checkcat进行类型转换.

所以,集合类的foreach方法语法糖可以翻译成如下代码:

1
2
3
while(iterator.hasNext()){
System.out.println(iterator.next());
}

switch语法糖

switch-String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo05_3 {
public static void choose(String str){
switch (str){
case "武松":
System.out.println("打老虎");
break;
case "鲁智深":
System.out.println("倒拔垂杨柳!");
break;
default:
System.out.println("智取生辰纲");
}
}
}

然后我们根据javap反编译

我们直接翻译字节码,翻译后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void choose(String str){
byte i = -1;
switch (str.hashCode()){
case 878808:
if(str.equals("武松")){
i= 0;
}
break;
case 39343864:
if(str.equals("鲁智深")){
i=2;
}
break;
default:

}
switch (i){
case 0:
System.out.println("打老虎");
break;
case 1:
System.out.println("倒拔垂杨柳!");
break;
default:
System.out.println("智取生辰纲");
}
}

可以,看出jvm将一个switch 拆分成两个switch来实现,然后再编译期,已经可以知道字符串的hashCode.

但是因为可能出现两个字符串hashCode相同,但是不是同一个字符串的情况,所以再case中进行二次确认.

为什么要在第一遍时比较hashCode,又要利用equals进行比较呢?

这是因为利用hashCode可以提高比较效率,减少可能的比较;而equals比较是为了防止hashCode冲突.

switch-enum

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo05_4 {
public static void printSex(Sex sex){
switch (sex){
case MALE:
System.out.println("男");
case FEMALE:
System.out.println("女");
}
}
}
enum Sex {
MALE,FEMALE
}

反编译后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

public static void printSex(demo05.Sex);
descriptor: (Ldemo05/Sex;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field demo05/Demo05_4$1.$SwitchMap$demo05$Sex:[I
3: aload_0
4: invokevirtual #3 // Method demo05/Sex.ordinal:()I
7: iaload
8: lookupswitch { // 2
1: 36
2: 44
default: 52
}
36: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
39: ldc #5 // String nan
41: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
47: ldc #7 // String 女
49: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
52: return
LineNumberTable:
...
}
1
0: getstatic     #2                  // Field demo05/Demo05_4$1.$SwitchMap$demo05$Sex:[I

这里可以看出,jvm帮我们生成了一个名叫Demo05_4$1的内部类

其中有一个int类型的数组

转换后这个内部类的代码如下:

1
2
3
4
5
6
7
static class Demo05_4$1{
static int[] map = new int[2]
static{
map[Sex.MALE.ordinal()] = 1
map[Sex.FEMALE.ordinal()] = 2
}
}

然后 利用一个byte类型的swatch进行操作.

枚举类

1
2
3
4

public enum Sex {
MALE,FEMALE
}

由于字节码比较多,我们直接看转换后代码.代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public final class Sex extends Enum<Sex>{

public static final Sex MALE;
public static final Sex FEMALE;
private static final Sex[] $values;
static {
MALE = new Sex("MALE",0);
FEMALE = new Sex("FEMALE",1);
$values = new Sex[]{MALE, FEMALE};
}
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
*/
private Sex(String name, int ordinal) {
super(name, ordinal);
}


public static Sex[] values() {
return $values;
}

public static Sex valueOf(String name){
return Enum.valueOf(Sex.class,name);
}
}

这就是枚举类的jvm实现.

0%