什么叫语法糖?
语法糖既是指在计算机语言中添加某种语法,这个语法本生对程序来说并没有什么坏处,只是方便程序的开发,说白了语法糖就是对现有语法的一种封装。再直白一点来说就是用这个语言本身的语法封装一大段的代码之后然后再上层使用的时候只需要用一个简单的操作就可以完成一系列浮躁的操作,而这些操作开发者可能并不知道他最终是如何使用语法糖衣的。
java中的语法糖(基于java1.7):
泛型
自动装箱和拆箱
枚举
内部类
长循环遍历
变长参数
try语句使用资源
断言
switch的字符串支持
泛型与类型擦除:
Java语言并不是一开始就支持泛型的。在早期的JDK中,只能通过Object类是所有类型的父类和强制类型转换来实现泛型的功能。强制类型转换的缺点就是把编译期间的问题延迟到运行时,JVM并不能为我们提供编译期间的检查。
在JDK1.5中,Java语言引入了泛型机制。但是这种泛型机制是通过类型擦除来实现的,即Java中的泛型只在程序源代码中有效(源代码阶段提供类型检查),在编译后的字节码中自动用强制类型转换进行替代。也就是说,Java语言中的泛型机制其实就是一颗语法糖,相较与C++、C#相比,其泛型实现实在是不那么优雅。
但是Java的泛型和C++/C#的泛型本质上是不一样的,C++/C#的泛型是一个占位符,在运行期都是存在的,比如List<Integer>
和List<String>
是不同的类型,在运行期都有自己的虚方法表和类型数据,这种实现才是真正的泛型,但是在Java里,泛型就是一个语法糖,只是在源码内存在,在编译后的字节码文件中,就已经替换为原来的原生类型(Object)了,也就是说在运行期中,List<Integer>
和List<String>
在运行期就变成了List<Object>
和List<Object>
,就是相同的类型,这种泛型就是伪泛型了,这种也是泛型擦除。
public class test{
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("hello", "沃德");
System.out.println(map.get("hello"));
}
}
反编译后的代码:
public class test{
public static void main(String[] var0) {
HashMap var1 = new HashMap();
var1.put("hello", "沃德");
System.out.println((String)var1.get("hello"));
}
}
可以看到泛型被擦除且引入强制类型转换,这样做的目的存在一定的缺点,应为进行类型转换会对性能有一定的影响
自动装箱和拆箱:
什么叫做自动拆箱和装箱?
我们都知道java中出了基本的数据类型还有一些这些基本数据类型的包装类,比如Interger等当使用语法糖之后就可以对这些基本类型和包装类型进行拆箱(使用Integer.intValue())和装箱(使用Interger.valueOf()),这样就很方便了比如你定义一个包装类的int,那就可以使用下面的语法来定义,在反编译后他就会自动装箱将i包装成Interger的类型,使用的时候拆箱就行了。
Interger i=10;
来看一个典型的例子:
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d);
System.out.println(e==f);
System.out.println(c==(a+b));
System.out.println(c.equals(a+b));
System.out.println(g==(a+b));
System.out.println(g.equals(a+b));
System.out.println(g.equals(a+h));
}
}
先别看输出结果,读者自己想一下这段代码的输出结果是什么。这里面需要注意的是:当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。明白了这2点之后,上面的输出结果便一目了然:
true
false
true
true
true
false
true
第一个和第二个输出结果没有什么疑问。第三句由于 a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。
后面的就不一一总结了,有兴趣的同学可以使用自己时间验证一下,写一段代码然后反编译一下就可以看到。