梦想还是要有的,万一忘了咋办?

0%

Java语法糖

概述

  • 几乎所有语法都有语法糖;
  • 无实质性的功能,方便开发者;
  • 或能提高编码效率,或能提供可读性、严谨性;

常见语法糖

泛型与类型擦除

泛型思想最早出现在C++,JDK1.5引进,本质是参数化类型(Parametersized Type)。包含泛型接口、泛型类、泛型方法。Java无泛型时,经常需要开发者强制转换Object类型,运行期间必须对这些风险做处理。泛型出现后将这些风险前置到编译期间。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
//不使用泛型
List array=new ArrayList();
array.add(“abc”);
array.add(123);//编译通过
String v=(String)array.get(0);//处理ClassCastException异常

//使用泛型
List<String> array2=new ArrayList();
array2.add("abc");
array.add(123);//编译不通过
String v2=array2.get(0);

自动装箱、拆箱与遍历循环

  • 包装类的"=="运算在不遇到算数运算的情况下不会自动拆箱
  • equals方法不处理数据类型转换的关系。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 自动装箱、拆箱
* <p>
* a==a1 : true
* c.equals(c1) : false
* d.equals(a1 + b1) : false
* d==a1+b1 : true
*/
@Test
public void boxOrUnbox() {
int a = 1;
Integer a1 = 1;
int b = 2;
Integer b1 = 2;
Integer c = 300;
Long c1 = 300l;
Long d = 3L;

System.out.println("a==a1 : " + (a == a1));
System.out.println("c.equals(c1) : " + (c.equals(c1)));
System.out.println("d.equals(a1 + b1) : " + (d.equals(a1 + b1)));
System.out.println("d==a1+b1 : " + (d == (a1 + b1)));

}

条件编译

通过推理可以预测到用于不会执行的代码会在编译时丢掉。

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
/**
* 条件编译
* <p>
*/
@Test
public void conditionCompile() {

if (false) {
System.out.println("222222");
}
System.out.println("33333");

}

 //编译后字节码
// access flags 0x1
public conditionCompile()V
@Lorg/junit/Test;()
L0
LINENUMBER 85 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "33333"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 87 L1
RETURN
L2
LOCALVARIABLE this Lcom/hardydou/lang/GrammarSugarTest; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1

此语法糖只针对if有效,其它条件语(while、for、switch)句会提醒无法编译提示:Unreachable statement

示例代码:

1
2
3
for (; false; ) {}
while (false) {}

内部类

枚举

  • 是一个类
  • 集成enum类
  • 是一个final 类
1
2
3
4
5
enum EType {
A,
B,
C;
}

编译后

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// class version 52.0 (52)
// access flags 0x4030
// signature Ljava/lang/Enum<Lcom/hardydou/lang/EType;>;
// declaration: com/hardydou/lang/EType extends java.lang.Enum<com.hardydou.lang.EType>
final enum com/hardydou/lang/EType extends java/lang/Enum {

// compiled from: GrammarSugarTest.java

// access flags 0x4019
public final static enum Lcom/hardydou/lang/EType; A

// access flags 0x4019
public final static enum Lcom/hardydou/lang/EType; B

// access flags 0x4019
public final static enum Lcom/hardydou/lang/EType; C

// access flags 0x101A
private final static synthetic [Lcom/hardydou/lang/EType; $VALUES

// access flags 0x9
public static values()[Lcom/hardydou/lang/EType;
L0
LINENUMBER 131 L0
GETSTATIC com/hardydou/lang/EType.$VALUES : [Lcom/hardydou/lang/EType;
INVOKEVIRTUAL [Lcom/hardydou/lang/EType;.clone ()Ljava/lang/Object;
CHECKCAST [Lcom/hardydou/lang/EType;
ARETURN
MAXSTACK = 1
MAXLOCALS = 0

// access flags 0x9
public static valueOf(Ljava/lang/String;)Lcom/hardydou/lang/EType;
L0
LINENUMBER 131 L0
LDC Lcom/hardydou/lang/EType;.class
ALOAD 0
INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
CHECKCAST com/hardydou/lang/EType
ARETURN
L1
LOCALVARIABLE name Ljava/lang/String; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 1

// access flags 0x2
// signature ()V
// declaration: void <init>()
private <init>(Ljava/lang/String;I)V
L0
LINENUMBER 131 L0
ALOAD 0
ALOAD 1
ILOAD 2
INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
RETURN
L1
LOCALVARIABLE this Lcom/hardydou/lang/EType; L0 L1 0
MAXSTACK = 3
MAXLOCALS = 3

// access flags 0x8
static <clinit>()V
L0
LINENUMBER 132 L0
NEW com/hardydou/lang/EType
DUP
LDC "A"
ICONST_0
INVOKESPECIAL com/hardydou/lang/EType.<init> (Ljava/lang/String;I)V
PUTSTATIC com/hardydou/lang/EType.A : Lcom/hardydou/lang/EType;
L1
LINENUMBER 133 L1
NEW com/hardydou/lang/EType
DUP
LDC "B"
ICONST_1
INVOKESPECIAL com/hardydou/lang/EType.<init> (Ljava/lang/String;I)V
PUTSTATIC com/hardydou/lang/EType.B : Lcom/hardydou/lang/EType;
L2
LINENUMBER 134 L2
NEW com/hardydou/lang/EType
DUP
LDC "C"
ICONST_2
INVOKESPECIAL com/hardydou/lang/EType.<init> (Ljava/lang/String;I)V
PUTSTATIC com/hardydou/lang/EType.C : Lcom/hardydou/lang/EType;
L3
LINENUMBER 131 L3
ICONST_3
ANEWARRAY com/hardydou/lang/EType
DUP
ICONST_0
GETSTATIC com/hardydou/lang/EType.A : Lcom/hardydou/lang/EType;
AASTORE
DUP
ICONST_1
GETSTATIC com/hardydou/lang/EType.B : Lcom/hardydou/lang/EType;
AASTORE
DUP
ICONST_2
GETSTATIC com/hardydou/lang/EType.C : Lcom/hardydou/lang/EType;
AASTORE
PUTSTATIC com/hardydou/lang/EType.$VALUES : [Lcom/hardydou/lang/EType;
RETURN
MAXSTACK = 4
MAXLOCALS = 0
}

断言语句

在Java中,assert关键字是从JAVA SE 1.4 引入的,为了避免和老版本的Java代码中使用了assert关键字导致错误,Java在执行的时候默认是不启动断言检查的(这个时候,所有的断言语句都将忽略!)。如果要开启断言检查,则需要用开关-enableassertions或-ea来开启。

1
2
3
4
5
6
7
8
9
10
/**
* 测试断言语法糖
*/
@Test
public void assertTest() {
int c = 2;
assert c != 1 + 1;
System.out.println("1111");

}

编译后

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
// access flags 0x1
public assertTest()V
@Lorg/junit/Test;()
L0
LINENUMBER 140 L0
ICONST_2
ISTORE 1
L1
LINENUMBER 141 L1
GETSTATIC com/hardydou/lang/GrammarSugarTest.$assertionsDisabled : Z
IFNE L2
ILOAD 1
ICONST_2
IF_ICMPNE L2
NEW java/lang/AssertionError
DUP
INVOKESPECIAL java/lang/AssertionError.<init> ()V
ATHROW
L2
LINENUMBER 142 L2
FRAME APPEND [I]
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "1111"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L3
LINENUMBER 144 L3
RETURN

switch对字符串的支持

jdk1.7 switch开始支持String类型,其实switch并未修改。只是在String编译的时候通过hashcode转换为long类型而已。
示例代码:

1
2
3
4
5
6
7
8
@Test
public void switchAndStr() {
switch ("abc") {
case "abc":
System.out.println("abc");
}

}

反编译后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void switchAndStr() {
String var1 = "abc";
byte var2 = -1;
switch(var1.hashCode()) {
case 96354:
if (var1.equals("abc")) {
var2 = 0;
}
default:
switch(var2) {
case 0:
System.out.println("abc");
default:
}
}
}

try语句中定义和关闭资源

Java 7开始,jdk提供了一种更好的方式关闭资源,使用try-with-resources语句
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 资源关闭语法糖
*/
@Test
public void try_with_resource() {

try (BufferedReader br = new BufferedReader(new FileReader(""))) {

br.readLine();

} catch (Exception e) {

}

}

反编译后

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
@Test
public void try_with_resource() {
try {
BufferedReader br = new BufferedReader(new FileReader(""));
Throwable var2 = null;
try {
br.readLine();
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (br != null) {
if (var2 != null) {
try {
br.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
br.close();
}
}

}
} catch (Exception var14) {
}

}

for-each

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* for-each语法糖
*/
@Test
public void forEach() {
String[] ss = new String[]{"a", "b", "c"};
for (String s : ss) {
System.out.println(s);
}
for (String s : Arrays.asList(ss)) {
System.out.println(s);
}
}

反编译后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void forEach() {
String[] ss = new String[]{"a", "b", "c"};
String[] var2 = ss;
int var3 = ss.length;

for(int var4 = 0; var4 < var3; ++var4) {
String s = var2[var4];
System.out.println(s);
}

Iterator var6 = Arrays.asList(ss).iterator();

while(var6.hasNext()) {
String s = (String)var6.next();
System.out.println(s);
}

}

字面量

示例代码

1
2
3
4
5
6
7
8
9
/**
* 字面量
*/
@Test
public void zimianliang() {
int a = 10_00;
int b = 1000;
System.out.println(a == b);
}

反编译后

1
2
3
4
5
6
@Test
public void zimianliang() {
int a = 1000;
int b = 1000;
System.out.println(a == b);
}

方法变长参数

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void classTest() {
build("a", "c", "b");
}

public static String build(String... strs) {
String r = "";
for (String s : strs) {
r += s;
}
return r;
}

反编译后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void classTest() {
build("a", "c", "b");
}

public static String build(String... strs) {
String r = "";
String[] var2 = strs;
int var3 = strs.length;

for(int var4 = 0; var4 < var3; ++var4) {
String s = var2[var4];
r = r + s;
}
return r;
}

语法糖坑

  • 泛型重载
1
2
3
4
5
//无法通过编译
public static void m1(List<String> list) {
   }
public static void m1(List<Integer> list) {
}
  • 泛型不可用于exception
1
2
3
4
5
6
7
8
9
10
//以下代码是错误示例
//被擦除后 两个都是MyException 无法分开。
try{
...
}catch(MyException<Integer> e){
...
}catch(MyException<String> f){
....
}

  • 泛型的静态变量
1
2
3
4
5
6
7
8
9
10
11
class O<T>{
public static var=0;
}

O<String> o1=new O();
o1.var=1;
O<Intger> o2=new O();
o2.var=2;

//此时,o1.var 跟 o2.var 是同一个。

  • 自动拆箱-对象相等容易混淆

适用于整数值区间-128 至 +127。
只适用于自动装箱。使用构造函数创建对象不适用。

1
2
3
4
5
6
Integer a2 = 1000;
Integer b2 = 1000;
System.out.println("a2==b2 is " + (a2 == b2));//false
Integer a3 = 127;
Integer b3 = 127;
System.out.println("a3==b3 is " + (a3 == b3));//true