语法糖,又称语法糖衣,是英国计算机科学家发明的一个术语,指在计算机语言中加入某种语法,这种语法对语言的功能并没有影响,但是方便了程序员的操作,并且增加了代码的可读性,减少了出错的机会。
Java在现代编程语言中,属于“低糖语言”(相对于JVM和c#)来说,尤其在JDK1.5之前,语法糖很少出现。Java中最常用的语法糖主要是泛型、变长参数、自动装箱、拆箱等,虚拟机运行时不支持这些语法糖操作,他们在编译后将还原为简单的语法结构,这个过程称为解语法糖。
泛型技术在Java和C#中看来很相似,但是他们有本质的区别。C#中,泛型无论在源代码、编译后的IL或者是在CLR中,都是切实存在的,List
Java语言中则不一样,在编译后的字节码中,就已经替换为原生类型了,并且在相应的地方加入了强制转换代码, 因此对于运行期的Java语言来说,ArrayList
来看下面一个使用泛型的代码
public class Sugar
{
public static void main(String[] args)
{
Map map=new HashMap();
map.put("miss", 0);
map.put("you", 2);
System.out.println(map.get("miss"));
}
}
对它的字节码进行反编译
public class Sugar
{
public static void main(String[] paramArrayOfString)
{
HashMap localHashMap = new HashMap();
localHashMap.put("miss", Integer.valueOf(0));
localHashMap.put("you", Integer.valueOf(2));
System.out.println(localHashMap.get("miss"));
}
}
反编译之后发现发现发型都不见了,程序变成了Java泛型出现之前的写法,
当我们定义以下两个两个方法的时候,按照我们的知识基础以及Java语法糖的知识,它是不能被编译通过的,因此这两个方法的方法签名是一样的
public class ReloadTest2
{
public void method1(List arg1)
{
}
public void method1(List arg2)
{
}
}
但再来看这样一个例子,再使用SDK的javac命令时是可以编译通过的,而且也能够运行。
public String method1(List arg1)
{
return null;
}
public int method1(List arg2)
{
return 0;
}
对它的字节码进行反编译,似乎又看到了泛型。
public class ReloadTest1
{
public String method1(List paramList)
{
return null;
}
public int method1(List paramList)
{
return 0;
}
}
那这是什么原因呢?我们知道返回值是不参与重载的,但是如果两个方法有相同的方法签名,但是返回值不同的话,它们是可以共存在一个Class文件中的。其实这样是毫无意义的,这只是一种“妥协”的方式,对于编程没有任何意义,甚至还带来了混乱。因此对于Eclipse来说,依然拒绝编译上面的代码
直接来看编译前后的代码对比,就可以看到是如何实现语法糖的了。
编译前
public static void main(String[] args)
{
List list=new ArrayList();
list.add(500);
list.add(2);
list.add(new Integer(20));
for(int sub:list)
{
System.out.println(sub);
}
}
反编译
public static void main(String[] args)
{
List list = new ArrayList();
list.add(Integer.valueOf(500));
list.add(Integer.valueOf(2));
list.add(new Integer(20));
for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ) { int sub = ((Integer)localIterator.next()).intValue();
System.out.println(sub);
}
}
可以看到,自动装箱的实现就是调用了Integer.valueOf方法,而foreach实际上是在调用list的iterator进行遍历。这也就是为了foreach方法要求被遍历的集合必须实现了Iterator接口。
来看看什么叫条件编译。
编译前
public static void main(String[] args)
{
if(true)
{
System.out.println("true");
}
else {
System.out.println("false");
}
}
编译后
public class ConditionCompiler
{
public static void main(String[] args)
{
System.out.println("true");
}
}
这里Java编译器对代码进行了简化,它根据布尔常量的真假,去掉了分支不成的代码块。只有使用条件为常量的If语句才能达到上面的效果,如果使用常量与其他类型的流程控制语句,则可能会编译出错,拒绝编译。