java虚拟机基础知识大全 java虚拟机运行原理(11)


常量折叠( )的代码优化:在代码里面定义“a=1+2”比起直接定义“a=3”来,并不会增加程序运行期哪怕仅仅一个处理器时钟周期的处理工作量 。
2.数据及控制流分析
数据流分析和控制流分析是对程序上下文逻辑更进一步的验证,它可以检查出诸如程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题 。
泛型的本质是参数化类型( Type)或者参数化多态(sm)的应用,即可以将操作的数据类型指定为方法签名中的一种特殊参数,这种参数类型能够用在类、接口和方法的创建中,分别构成泛型类、泛型接口和泛型方法 。
Java选择的泛型实现方式叫作“类型擦除式泛型”(Type),而C#选择的泛型实现方式是“具现化式泛型”( ) 。
Java语言中的泛型则不同,它只在程序源码中存在,在编译后的字节码文件中,全部泛型都被替换为原来的裸类型(Raw Type,稍后我们会讲解裸类型具体是什么)了,并且在相应的地方插入了强制转型代码,因此对于运行期的Java语言来说,与其实是同一个类型
在没有泛型的时代,由于Java中的数组是支持协变()的,引入泛型后可以选择:
1)需要泛型化的类型(主要是容器类型),以前有的就保持不变,然后平行地加一套泛型化版本的新类型 。
2)直接把已有的类型泛型化,即让所有需要泛型化的已有类型都原地泛型化,不添加任何平行于已有类型的泛型版 。
我们继续以为例来介绍Java泛型的类型擦除具体是如何实现的 。由于Java选择了第二条路,直接把已有的类型泛型化 。要让所有需要泛型化的已有类型,譬如,原地泛型化后变成了,而且保证以前直接用的代码在泛型新版本里必须还能继续用这同一个容器,这就必须让所有泛型化的实例类型,譬如、这些全部自动成为的子类型才能可以,否则类型转换就是不安全的 。由此就引出了“裸类型”(Raw Type)的概念,裸类型应被视为所有该类型泛型化实例的共同父类型(Super Type) 。
如何实现裸类型 。这里又有了两种选择:一种是在运行期由Java虚拟机来自动地、真实地构造出这样的类型,并且自动实现从派生自的继承关系来满足裸类型的定义;另外一种是索性简单粗暴地直接在编译时把还原回,只在元素访问、修改时自动插入一些强制类型转换和检查指令 。
基于这种方法实现的泛型称为伪泛型 。
void main([] args){
Map map=new ();
map.put("hello","nihao");
map.put("how are you","");
.out.(map.get("hello"));
.out.(map.get("how are you"));
}
这段代码编译成Class文件,然后用字节码反编译工具进行反编译后,泛型类型都变回了原生类型
void main([] args){
Map map=new ();
map.put("hello","nihao");
map.put("how are you","");
.out.(()map.get("hello"));
.out.(()map.get("how are you"));
}
java泛型擦除式实现的缺陷:
1.对原始类型( Types)数据的支持又成了新的麻烦,既然没法转换那就索性别支持原生类型的泛型了吧,你们都用、,反正都做了自动的强制类型转换,遇到原生类型时把装箱、拆箱也自动做了得了 。这个决定后面导致了无数构造包装类和装箱、拆箱的开销,成为Java泛型慢的重要原因,也成为今天项目要重点解决的问题之一 。
2.运行期无法取到泛型类型信息 。
由于List和List擦除后是同一个类型,我们只能添加两个并不需要实际使用到的返回值才能完成重载 。
另外,从属性的出现我们还可以得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们在编码时能通过反射手段取得参数化类型的根本依据 。