概述
语法糖(Syntactic Sugar):主要作用是提高编码效率,减少编码出错的机会。
解语法糖发生在Java源码被编译成Class字节码的过程中,还原回简单的基础语法结构。
语法糖之一:泛型(Generics)
Java中的泛型又称为类型擦除,它只在Java源码中存在,被编译成字节码后,就已经替换为原生类型了,并在相应的地方加入强制类型转换。
例如:
public class GenericTypes { /* * 两个mothod1方法不能被编译,因为List<Integer>和List<String>被编译成class文件后都被擦除了, * 变成了一样的原生类型List<T>,擦除之后两个方法的签名一样。 */ public static void mothod1(List<Integer> list) { } public static void mothod1(List<String> list) { } /* * 在jdk1.7 * 两个mothod2方法不能被编译,因为List<Integer>和List<String>被编译成class文件后都被擦除了, * 变成了一样的原生类型List<T>,擦除之后两个方法的签名一样。返回值不参与重载选择 * * Sun JDK1.6中Javac才能编译成功 * 在Class文件格式中,只要描述符不是完全一致的两个方法就可以共存。 */ public static String mothod2(List<String> list) { return ""; } public static Integer mothod2(List<Integer> list) { return 1; } }
语法糖之二:自动拆箱和装箱、Foreach、变长参数
例如:
public class Foreach_Varargs { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4);//变长参数(Varargs) int sum = 0; for(int i : list) { //遍历循环Foreach sum += i; } System.out.println(sum); } /* * 反编译之后的代码 * 1、变长参数还原为数组类型的参数:Arrays.asList(...) ----> new Integer[]{...} * 2、Foreach还原为迭代器实现 * 3、自动拆箱和装箱还原为Integer.valueOf()和Integer.intValue()方法 * public static void main(String[] args) { java.util.List<Integer> list = java.util.Arrays.asList(new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) }); int sum = 0; for (Iterator localIterator = list.iterator(); localIterator.hasNext();) { int i = ((Integer) localIterator.next()).intValue(); sum += i; } System.out.println(sum); } */ }
一个更复杂的自动装箱拆箱的栗子:
public class Autoboxing { 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; System.out.println(c == d); //(1) System.out.println(e == f); //(2) System.out.println(c == (a + b)); //(3) System.out.println(c.equals(a + b)); //(4) System.out.println(g == (a + b)); //(5) System.out.println(g.equals(a + b)); //(6) } /* * 反编译后的代码 * * 包装类的“==”运算在不遇到算数运算的情况下不会自动拆箱; * equals方法不处理数据转型的关系。 * * 在 Java 中,== 比较的是对象引用,而 equals 比较的是值。 * * 一、(1)为true,(2)为false原因: * IntegerCache:把-128到127(可调)的整数都提前实例化了,不管创建多少个这个范围内的Integer用ValueOf出来的都是同一个对象; * 用来节省内存和提高性能。这种 Integer缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。 * 这个缓存会在 Integer 类第一次被使用的时候被初始化出来.是什么原因选择这个 -128 到 127 这个范围呢?因为这个范围的整数值是使用最广泛的。 * Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行 * * 不在缓存范围的会新new Integer对象。 * 二、(3)为true,(5)为true * 自动拆箱,相当于数值类型int * 三、(4)为true * a,b先拆箱计算数值和,再将计算结果装箱为Integer * 四、(5)为false * g为Long类型,a + b为Integer,类型不一致 * 五、(6)为false, g为Long类型,a + b为Integer,类型不一致,返回false public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; } * public class Autoboxing { public static void main(String[] args) { Integer a = Integer.valueOf(1); Integer b = Integer.valueOf(2); Integer c = Integer.valueOf(3); Integer d = Integer.valueOf(3); Integer e = Integer.valueOf(321); Integer f = Integer.valueOf(321); Long g = Long.valueOf(3L); System.out.println(c == d); System.out.println(e == f); System.out.println(c.intValue() == a.intValue() + b.intValue()); System.out.println(c.equals(Integer.valueOf(a.intValue() + b.intValue()))); System.out.println(g.longValue() == (long)(a.intValue() + b.intValue())); System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue()))); } } */ }
语法糖之三:条件编译
栗子:
public class ifdef { public static void main(String[] args) { final boolean isCompile = true; if(isCompile) { System.out.println("11111"); } else { System.out.println("2222"); } } /* * 条件编译 * System.out.println("2222"); 不会编译 * Java只能实现语句基本块级别的条件编译,而无法实现根据条件调整整个Java类的结构。 * * 反编译后的代码: * public class ifdef { public static void main(String[] args) { boolean isCompile = true; System.out.println("11111"); } } */ }