javac编译器
编译过程包括解析与填充符号表、注解处理、分析与字节码生成。
在编译过程中还会解语法糖,即把一些简化的形式还原为其原有的样子。
java语法糖
泛型
java的泛型是一种擦除式泛型,它只在程序源码中存在,在编译后的字节码中全部泛型都被替换为原来的裸类型,并加入了强制类型转换代码。
这种擦除式泛型的好处在于增加泛型只需要改动javac编译器,无需对字节码、虚拟机动刀,同时使泛型能过运行在没有泛型的java5.0之上。
java泛型的历史背景
想要增加泛型且不影响旧版本,有两种选择:
需要泛型化的类型(主要为容器类型),原有的保持不变,平行增加一套泛型化版本的新类型
直接把已有的类型泛型化
C#的泛型选择的是第一种方案,因为体量较小,但java体量大,且本身就有集合类的新老版本例如Vector、ArrayList、Hashtable、HashMap的老到新版本变化,如果再加新的支持泛型的类型,导致版本数量膨胀太严重。因此java选择了直接把已有类型进行泛型化的方案。
为了使老版本支持原地泛型化,java增加了裸类型(Raw Type)的概念,裸类型是所有该类型泛型化实例的共同父类型,这样才能安全进行类型转换。
如何实现裸类型,也有两种选择:
在运行期间由java虚拟机自动地、真实地构造出ArrayList<Integer>这样的类型,并实现ArrayList<Integer>派生自ArrayList的继承关系
编译时直接把ArrayList还原位ArrayList,只在元素访问、修改时自动插入强制类型转换和检查指令
java选择了第二种方案,因此在字节码的反编译中可以看到如下这种:
public static void main(String[] args) throws Throwable {
Map map = new HashMap();
map.put("hello", "你好");
map.put("how are you?", "吃了吗");
System.out.println((String) map.get("hello"));
System.out.println((String) map.get("how are you?"));
}
第二种方案虽然简单,但也带来了一些问题:
泛型擦除法对原始类型数据的支持产生了问题,因为遇到强制转型代码的地方没发做,不支持int、long、Object之间的强制转型,因此java索性就不支持原生类型泛型,直接用ArrayList<Integer>等,顺便把装箱也做了
运行期间获取不到泛型信息,因此无法直接把泛型作为getClass的目标,必须再传一个Class<T>参数代表传入泛型的类型,才能在运行期间正确获取到对应类型。
评论区