Java虚拟机——前端编译优化

  • Java的编译期是有上下文语境影响的,不同语境下可以指不同的过程:
  1. 可以是前端编译器,把*.java文件转变成*.class文件的过程。
  • JDK的Javac、Eclipse JDT中的增量式编译器
  1. 可以指Java虚拟机的即时编译器(JIT编译器)在运行期将字节码转变成本地机器码的过程
  • HostSpot虚拟机的C1、C2编译器、Graal编译器
  1. 还可以指使用静态的提前编译器(AOT编译器)直接把程序编译成与目标机器指令集相关的二进制代码过程
  • JDK的Jaotc、GNU Compiler for the Java 、 Excelsior JET

  • 本章的编译期和编译器都指第一类,前端编译器。

1 Javac编译器

  • Javac编译器不像HotSpot虚拟机那样使用C++语言(包含少量C语言)来实现,它本身就是一个由Java语言编写的程序。
  • 从Javac代码的总体结构来看,编译过程大致可以分为1个准备过程和3个处理过程。
  1. 准备过程:初始化插入式注解处理器
  2. 解析与填充符号表过程
  • 词法、语法分析,将源代码的字符流转变为标记集合,构造出抽象语法树。
  • 填充符号表,产生符号地址和符号信息。
  1. 插入式注解处理器 的注解处理过程
  • 在JDK 6中可以提前到编译期对代码中的特定注解进行处理,影响前段编译器的工作过程
  1. 分析与字节码生成过程
  • 标注检查,对语法的静态信息进行检查
  • 数据流及控制流分析,对程序动态运行过程进行检查
  • 解语法糖,将语法糖还原为原有形式
  • 字节码生成

2 Java语法糖

2.1 泛型

  • 泛型的本质是参数化类型或者参数化多态的应用,即可以将操作的数据类型指定为方法签名中的一种特殊参数。

  • 这种参数类型能够在类、接口和方法的创建中,分别构成了泛型类、泛型接口和泛型方法。

  • Java选择的泛型实现方式叫做"类型擦除式泛型",C#选择的是"具现化式泛型"

  • Java的泛型在编译后的字节码文件中,全部泛型都被替换为原来的裸类型(Raw Type),并且在相应的地方插入了强制转型代码。
    Java虚拟机——前端编译优化_第1张图片

public class TypeErasureGenerics<E>{
    public void doSomething(Object item){
        if(item instanceof E){ //不合法,无法对泛型进行实例判断
            ...
        }
        E newItem = new E(); //不合法,无法使用泛型创建对象
        E[] itemArray = new E[10]; //不合法,无法使用泛型创建数组
    }
}

2.2 类型擦除

  • 要让以前写在ArrayList的代码在泛型新版本里必须还能继续用这同一个容器,就必须让ArrayList、ArrayList这些全部自动成为ArrayList的子类型
  • 所以类型泛型化实例 的 共同父类型 就被称为裸类型。
import java.util.ArrayList;

public class GenericsDemo {
    public static void main(String[] args) {
        ArrayList<Integer> iList = new ArrayList<Integer>();
        ArrayList<String> sList = new ArrayList<String>();
        ArrayList list; //裸类型
        list = iList;
        list = sList;
    }
}
  • Java是如何实现裸类型的呢?虚拟机并没有真实构造出ArrayList这样的类型。 而是直接粗暴地将ArrayList在编译时还原回ArrayList,在我们需要对元素访问、修改时 自动插入一些强制类型转换和检查指令。

2.3 类型擦除的坏处

  1. 运行期间无法取到泛型类型信息
  2. 不支持原生类型的泛型,因为Java不支持int、long与Object之间的强制转换。但是用了包装类Integer、Long之后就会多了包装类装箱和拆箱的开销。
  3. 重载方法上带来的麻烦
  • 这两个参数之后都被擦除了,变成了同一种裸类型的List,所以这两个方法的特征签名变得一样,无法重载。
public static void method(List<String> list){
    System.out.println("invoke method String");
}
public static void method(List<Integer> list){
    System.out.println("invoke method Integer");
}

2.4 自动装箱、拆箱、遍历循环

  • 通过反编译Class文件可以得到它们的本质实现
public static void aiBox(){
    //new Integer[]{Integer.valueOf(1) , ....} 自动装箱
    List<Integer> list = Arrays.asList(1,2,3,4);
    int sum = 0 ;
    for(int i : list){ //遍历循环
        sum += i; //自动拆箱
    }
    System.out.println(sum);
}

你可能感兴趣的:(java,前端,python)