sda
sdad【注】:
sdad①、什么叫类型检查呢? 就是一个对象引用对应的实例类型必须是静态类型的子类,而有的语言就不需要比如:JavaScript。只要对象引用对应的实例类型中的方法定义中确实包含有要执行的方法,能够找到相同签名的方法,调用便可成功。
sdad②、产生这种差异的根本原因(对于①): 是Java语言在编译期间却已将要执行的方法完整的符号引用生成出来,并作为方法调用指令的参数(操作数)存储到Class文件中。 而这个符号引用包含了该方法定义在哪个具体类型之中、方法的名字以及参数顺序、参数类型和方法返回值等信息,通过这个符号引用,Java虚拟机就可以翻译出该方法的直接引用。 (而JavaScript等动态类型语言与Java有一个核心的差异就是对象引用本身并没有类型,他的值才具有类型,所以编译器在编译时最多只能确定方法名称、参数、返回值这些信息,而不会去确定方法所在的具体类型(即方法接收者不固定)。“变量(对象引用)无类型而变量值才有类型”这个特点也是动态类型语言的一个核心特征。)
sda
静态类型语言与动态类型语言的比较?
sda
sdad①、静态类型语言能够在编译期确定变量类型,最显著的好处是编译器可以提供全面严谨的类型检查,这样与数据类型相关的潜在问题就能在编码时被及时发现,利于稳定性及让项目容易达到更大的规模。
sdad②、动态类型语言在运行期才确定类型,这可以为开发人员提供极大的灵活性。
sda
什么叫动态语言呢?
sda
sdad 运行期间,允许改变程序结构(例如引进新函数、删除旧函数)或变量类型。比如Python.
sda
什么叫强类型语言呢?什么叫弱类型语言?
sda
sdad强类型语言是一种强制类型定义的语言,一旦某一个变量被定义类型,如果不经过强制转换,则它永远就是该数据类型了,强类型语言包括Java、.net 、Python、C++等语言。
sda
sdad弱类型语言是一种弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。弱类型语言包括vb 、PHP、javascript等语言。
sda
sdad无论是强类型语言还是弱类型语言,判别的根本是是否会隐性的进行语言类型转变。强类型语言在速度上略逊于弱类型语言,但是强类型定义语言带来的严谨性又能避免不必要的错误。
sda
什么叫运行时异常?什么叫连接时异常?
sasdasdsad
sdad运行时异常: 只要代码不执行到这一行就不会出现问题;
sdad连接时异常: 即使导致连接时异常的代码放在一条根本无法被执行到的路径分支上,类加载时也照样会抛出异常。
sasdasdsad
sasdasdsadsadsadsadasdasdasdasdsadsadsaasddad异常代码示例
public static void main(String[] args) {
int[][][] array = new int[1][0][-1];
}
sdad分析:
sdad①、Java中是能正常编译的,但运行会出现NegativeArraySizeException
异常,《Java虚拟机规范》中规定是一个运行时异常。
sdad②、C中直接在编译期报错。
sdad总结:一门语言的哪一种检查行为要在运行期进行,哪一种检查要在编译期进行并没有什么必然的因果逻辑关系。
===========================================================================
背景介绍:
sdad 目前已经有许多动态类型语言运行于Java虚拟机之上了,如Clojure、Groovy、Jython和JRuby等,也就是说我们可以在JVM上实现静态类语言的严谨与动态类型语言的灵活。
sd ad但是JDK7以前,JVM层面对动态类型语言的支持一直都还有所欠缺,主要表现在方法调用方面,只有4条方法调用指令(invokevirtual、invokespecial、invokestatic、invokeinterface),且它们的第一个参数(操作数)都是被调用的方法的符号引用(CONSTANT_Methodref_info或者CONSTANT_InterfaceMethodref_info常量)。
sda
sdad这时就有一个问题:方法的符号引用在编译时产生,而动态类型语言只有在运行期才能确定方法的接收者。
sda
sdad那怎么解决这个问题的呢?
sda
sdad ①、曲线救国:比如编译时留个占位符类型,运行时动态生成字节码实现具体类型到占位符类型的适配。但是这样有两个缺点:
sdaadⅠ、让动态类型语言实现的复杂度增加,也会带来额外的性能和内存开销sdadⅡ、由于无法确定调用对象的静态类型,而导致的方法内联无法有效进行。
sda
sdaad基于以上背景,在JVM层面上提供动态类型的直接支持就成为Java平台发展必须解决的问题,直到JDK 7,JSR-292提出了invokedynamic
指令以及java.lang.invoke
包解决了此问题。
sdad这个包的主要目的:在之前单纯依靠符号引用来确定调用的目标方法这条路之外,提供一种新的动态确定目标方法的机制,称为“方法句柄”(Method Handle)。
sda
sdad**[注]:** 在Java语言中做不到像C那样,单独把一个函数作为参数(函数指针的功劳)进行传递。普遍的做法是设计一个带有compare()方法的Comparator接口,以实现这个接口的对象作为参数。比如void sort(List list, Comparator c)
。
sda
sdad不过在有了方法句柄之后,Java语言就可以像C那样,拥有类似于函数指针或者委托的方法别名这样的工具了。
sasdasdsad
sasdasdsadsadsadsadasdasdasdasdsadsadsaasddad方法句柄演示
import static java.lang.invoke.MethodHandles.lookup;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
public class MethodHandleTest {
static class ClassA {
public void println(String s) {
System.out.println(s);
}
}
public static void main(String[] args) throws Throwable {
Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA();
// 无论obj最终是哪个实现类,下面这句都能正确调用到println方法。
getPrintlnMH(obj).invokeExact("icyfenix");
}
private static MethodHandle getPrintlnMH(Object reveiver) throws Throwable {
// MethodType:代表“方法类型”,包含了方法的返回值(methodType()的第一个参数)
//和具体参数(methodType()第二个及以后的参数)。
MethodType mt = MethodType.methodType(void.class, String.class); //pritln的返回值、以及具体参数
/*lookup()方法来自于MethodHandles.lookup,这句的作用是在指定类中查找(符合给定的方法名称、
方法类型,并且符合调用权限的)方法句柄。
因为这里调用的是一个虚方法,按照Java语言的规则,方法第一个参数是隐式的,代表该方法的接收者,
也即this指向的对象,这个参数以前是放在参数列表中进行传递,现在提供了bindTo()方法来完成这件事情。
*/
return lookup().findVirtual(reveiver.getClass(), "println",mt).bindTo(reveiver);//方法类型、名称、即返回值和参数
}
}
方法解析:
sdad方法getPrintlnMH()中实际上是模拟了invokevirtual指令的执行过程,只不过它的分派逻辑并非固化在Class文件的字节码上,而是通过一个由用户设计的Java方法来实现。而这个方法本身的返回值(MethodHandle对象),可以视为对最终调用方法的一个“引用”。
sdad我们再次回顾sort()方法,有了MethodHandle就可以写出类似于C/C++那样的函数声明了:
sdasdasdasdasdsadasdsadasdasdasdasadvoid sort(List list, MethodHandle compare)
到此,我们发现MethodHandle在使用方法和效果上与Reflection有众多相似之处。但是它们也有 很大的区别:
sasad①、Reflection和MethodHandle机制本质上都是在模拟方法调用。但是Reflection是在模拟Java代码层次的方法调用,而MethodHandle是在模拟字节码层次的方法调用。在MethodHandles.Lookup上的3个方法findStatic()、findVirtual()、findSpecial()正是为了对应于invokestatic、invokevirtual(以及invokeinterface)和invokespecial这几条字节码指令的执行权限校验行为,而这些底层细节在使用Reflection API时是不需要关心的。
sasad②、Reflection中的java.lang.reflect.Method对象远比MethodHandle机制中的java.lang.invoke.MethodHandle对象所包含的信息来得多。
ssadasdasad前者是方法在Java端的全面映像,包含了方法的签名、描述符以及方法属性表中各种属性的Java端表示方式,还包含执行权限等的运行期信息。
ssadasdasad后者仅包含执行该方法的相关信息。
sasad③、Reflection API的设计目标是只为Java语言服务的,而MethodHandle则设计为可服务于所有Java虚拟机之上的语言。