深入理解Java常用语法糖
语法糖的存在主要是方便开发人员使用,使程序更加简洁,提高程序可读性。Java虚拟机并不支持这些语法糖,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
语法糖的存在主要是方便开发人员使用,使程序更加简洁,提高程序可读性。Java虚拟机并不支持这些语法糖,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
文章目录
1. Java7新增switch对String的支持
String str = "awecoder";
switch (str) {
case "awecoder":
System.out.println(str);
break;
default:
System.out.println("other");
}
Jad反编译后
String str = "awecoder";
String s = str;
byte byte0 = -1;
switch(s.hashCode())
{
case -1520954730:
if(s.equals("awecoder"))
byte0 = 0;
break;
}
switch(byte0)
{
case 0: // '\0'
System.out.println(str);
break;
default:
System.out.println("other");
break;
}
从代码中可以看出,switch(String)是通过hashCode()和equals()方法组合实现的。对字符串对象的哈希值做switch操作,并在内部使用equals方法做安全校验,避免哈希碰撞。hashCode方法返回的是int类型。由于用的是较新的jdk版本(1.8.0_221),反编译结果与很多博客有所不同,主要体现在第二次对byte的switch操作。
switch在JDK1.5新增对包装类型支持,JVM在编译时会进行拆箱操作,而枚举类是采用枚举类的ordinal方法,返回int类型。综上,swtich仅支持基本数据类型。
【问题】在同时运行swtich(String)和swtich(Integer)两个小案例时,jad反编译代码有如下几句。
String str = "awecoder";
Integer integer = str; // 把str引用直接赋值给了integer引用
switch(integer.hashCode()) {...}
2. 泛型
Java编译器在处理泛型时采用代码共享的方式,为每个泛型类型创建唯一的字节码表示。通过类型擦除的方式实现泛型类型到唯一字节码的映射。
类型擦除的操作过程是,将所有泛型参数用其最左边界类型替换,并移除所有类型参数。例如List<Map<String, Object>>
泛型类型,在反编译后的代码是List
。
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2); // true
在泛型代码的内部是无法获取到任何有关泛型类型参数信息的。在案例中,c1和c2得到的字节码是完全相同的,通过反编译我们可以get
其中的原因,c1
和c2
被赋值的均为((new ArrayList()).getClass()
。
3. 自动拆装箱
Java不是完全的面向对象语言,存在8种基本数据类型,它们不是对象,不需要通过new创建变量,变量直接存储值到栈中。为了更好的使用这些数据类型,Java分别提供了包装类。拆装箱就是实现基本数据类型与对象之间的转换。
Integer i = 42;
反编译后为
Integer i = Integer.valueOf(42);
4. 方法变长参数
对于调用方法的实参,会根据可变长参数的实际参数创建给定类型的数组,再把数组作为真正的实参传递给被调方法。
method(42, "awe","coder");
public static void method(int i, String...strs) {}
反编译后代码
method(42, new String[] {
"awe", "coder"
});
public static transient void method(int j, String as[]){}
其他语法糖:for-each/枚举/内部类/编译时优化/try-with-resource
枚举:对于枚举enum
,其不是新的类型,而是关键字。对于枚举类我们反编译可以得知,枚举类其实是创建了一个继承Enum
类的不可变类。
public static final class t extends Enum
内部类:是编译时的概念,编译成功后会生成外部类和内部类多个字节码文件。例如,例如上面的内部枚举类,在编译后会得到Test.clas
s和Test$t.class
文件。
编译时优化:JVM在编译时会优化可以优化的部分,例如常量。下面的例子中,如果i没有final修饰,只是普通变量,JVM便不会进行该优化。
final int i = 1;
int j = i + 1;
反编译后得到
int i = 1;
int j = 2;
另一个编译时优化的例子是条件编译。例子中的条件语句是不会被JVM编译的,同样此处也要求常量。
final boolean b = false;
if (b) {
System.out.println("Hello, b!");
}
反编译后为
boolean b = false;
for-each:其内部仍然是采用普通for循环和迭代器实现遍历。
try-with-resource是JDK1.7中新增的功能,用于代码更加优雅的关闭资源。
参考资料
- Hollis知识星球博文
- 《Java编程思想》
更多推荐
所有评论(0)