动态类型语言
随着JDK1.7的发布,字节码指令集迎来了一个新成员-invokedynamic指令,这条指令视为了实现“动态类型语言”支持而进行的改进之一。
什么是动态类型语言?动态类型语言的关键特征是他的类型检查的主体过程是在运行期而不是在编译期,满足这个条件的语言有很多比如PHP,Ruby,JavaScript等等,相对的,在编译期就进行类型检查过程的语言就是最常用的静态类型语言比如C++。
下面我们举两个例子来说说什么是“在编译期/运行期进行”和什么是类型检查。
public static void main(String[] args) {
int[][] array = new int[1][-1];
}
这段代码能够正常编译,但是运行的时候会报java.lang.NegativeArraySizeException异常,在java虚拟机规范中明确定义了java.lang.NegativeArraySizeException是一个运行时一行,通俗一点来说,运行时异常就是只要代码不运行到这一行就不会有问题。与运行时异常相对应的是连接时异常,例如很常见的NoClassDefFoundError便属于连接时异常,即使会导致连接时异常的代码放在一条无法执行到的分支路径上,类加载时(java的连接过程不再编译阶段,而在类加载阶段)也照样会抛出异常。
下面再来看看“类型检查”,如下一行代码
obj.println("hello word");
虽然我们能够明白这行代码想要做什么,但是对于计算机来说,这一行没头没尾的代码是无法执行的,他需要一个具体的上下文才有意义。
在java语言中,并且变量obj的静态类型为java.io.PrintStream,那么变量obj的实际类型即必须是PrintStream的子类(实现了PrintStream接口的类)才是合法的,否则哪怕obj属于一个确实有用println(String)方法,但与PrintStream接口没有继承关系,代码依然不能运行,因为类型检查不合法。
但是相同的代码在javaScript中可以运行,无论obj具体是何种类型,只要这种类型的定义中确实包含有println(String)方法,那方法调用便可成功。
这种差别产生的原因是java语言在编译期间已将println(String)方法完整的符号引用生成出来,作为方法调用指令的参数存储到Class文件中。
这个符号引用包含了此方法定义在哪个具体类型之中,方法的名字以及参数顺序、参数类型和方法返回值等信息,通过这个符号引用,虚拟机可以翻译出这个方法的直接引用。而在JavaScript等动态语言中,变量obj本身没有类型,变量obj的值才具有类型,编译时只能确定方法名称、参数、返回值这些信息,而不会去确定方法所在的具体类型(即方法的接收者不固定)。“变量无类型而变量值才有类型”这个特点也是动态类型语言的一个重要特征。
网友评论