Java是软件开发中的“银弹”么?不管你的观点是什么,都不可否认的是,Java确实为程序员做了很多事情:比如内存管理,安全沙箱,可移植性(真的可移植么?)等等。然而,java还有一些语法糖来帮助开发,下面就来谈谈这些语法糖。
"+","-","*"和"/"都是数值计算的经典符号,然而在Java中"+"号可以还可以用于连接String,而String是对象,为什么可以使用"+"号连接?请参考以下代码:
public class Test { public static void main(String[] args) { String str = "a" + "b" + "c"; for (int i = 0; i < 10; i++) { str += "d"; } System.out.println(str); } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { String str = "abc"; for (int i = 0; i < 10; i++) { str = (new StringBuilder(String.valueOf(str))).append("d").toString(); } System.out.println(str); } }
从以上代码可以看出:java编译器对String字面量做了优化,把"a" + "b" + "c"合并成了"abc";另外使用StringBuilder的append方法来替代了"+"号(现在明白为什么有时候使用F5进行debug时会自动进入StringBuilder的append方法了吧)。需要注意的是”str = (new StringBuilder(String.valueOf(str))).append("d").toString();“这段代码创建了两个对象:new StringBuilder创建了一个StringBuilder对象,最后的toString()创建了一个String对象;所以在for循环中最好不要使用"+"号或"+="来连接字符串。
Java提供了条件预编译功能,它能删除不可到达的分支,代码如下:
public class Test { public static void main(String... args) { if (true) { System.out.println("yes"); } else { System.out.println("wrong"); } } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { System.out.println("yes"); } }
从反编译的代码可以看出,不可能到达的else分支被删除了。
Tiger引入了变参函数,代码如下:
public class Test { public static void main(String... args) { doNothing(args); } private static void doNothing(String... params) { } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { doNothing(args); } private static void doNothing(String[] params) { } }
从反编译的代码可以看出,这又是编译器的功劳:将String...变参替换成了String[]数组。
Tiger引入了泛型,它可以在编译阶段校验集合元素类型的正确性,代码如下:
public class Test { public static void main(String... args) { List<String> strList = new ArrayList<>(); System.out.println(strList); } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { List strList = new ArrayList(); System.out.println(strList); } }
从反编译的代码可以看出,类型被擦除了,现在知道《Java编程思想》中提到的泛型类型擦除是什么意思了吧。
Tiger引入了for-each循环,用它可以方便地遍历数组、枚举和任何实现了Iterable接口的集合,代码如下:
public class Test { public static void main(String... args) { String[] strs = { "a", "b", "c" }; for (String s : strs) { s += s; } List<String> strList = new ArrayList<String>(); for (String s : strList) { s += s; } } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { String[] strs = { "a", "b", "c" }; String args1[]; int j = (args1 = strs).length; for(int i = 0; i < j; i++) String s = args1[i]; s = (new StringBuilder(String.valueOf(s))).append(s).toString(); } List strList = new ArrayList(); for(Iterator iterator = strList.iterator(); iterator.hasNext();) { String s = (String)iterator.next(); s = (new StringBuilder(String.valueOf(s))).append(s).toString(); } } }
从反编译代码可以看出,编译器使用普通的for循环替换了数组的for-each循环,并使用迭代器来替换了集合的for-each循环。
Tiger引入了自动装箱和自动拆箱,它能够根据上下文自动在基本类型和包装类型之间切换,代码如下:
public class Test { public static void main(String... args) { Integer i = 1; int j = i; System.out.println(j); } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { Integer i = Integer.valueOf(1); int j = i.intValue(); System.out.println(j); } }
从反编译代码可以看出:编译器在装箱时调用了Integer的valueOf函数,而在拆箱时使用了Integer的intValue函数。
Tiger引入了枚举类型,然而JVM规范是没有定义”枚举“类型的,所以枚举会被编译成普通Java类,代码如下:
public enum Test { TEST }
找出该类的.class文件并反编译它,结果如下:
public final class Test extends Enum { private Test(String s, int i) { super(s, i); } public static Test[] values() { Test atest[]; int i; Test atest1[]; System.arraycopy(atest = ENUM$VALUES, 0, atest1 = new Test[i = atest.length], 0, i); return atest1; } public static Test valueOf(String s) { return (Test)Enum.valueOf(sugar/Test, s); } public static final Test TEST; private static final Test ENUM$VALUES[]; static { TEST = new Test("TEST", 0); ENUM$VALUES = (new Test[] { TEST }); } }
从反编译代码可以看出:编译器将它编译成了Enum类的子类。
Java7引入了数字字面量,代码如下:
public class Test { public static void main(String... args) { int i = 10_000; System.out.println(i); } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { int i = 10000; System.out.println(i); } }
从反编译代码可以看出:编译器将数值的下划线删除了。
Java7的switch可以使用String类型了,代码如下:
public class Test { public static void main(String... args) { String s = "a"; switch (s) { case "a": break; } } }
找出该类的.class文件并反编译它,结果如下:
public class Test { public static void main(String[] args) { String s = "a"; String str1; switch ((str1 = s).hashCode()) { case 97: if (str1.equals("a")) break; } } }
从反编译代码可以看出:编译器通过String的hashCode()方法将String比较转换成了int比较。