目 录CONTENT

文章目录

编译期与优化

FatFish1
2025-04-21 / 0 评论 / 0 点赞 / 28 阅读 / 0 字 / 正在检测是否收录...

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>参数代表传入泛型的类型,才能在运行期间正确获取到对应类型。

0

评论区