重新学习javaSE——类型系统

1.java识别类型信息通过两种方式:一是RTTI,所谓的运行时类型识别,另一种是反射。

2.RTTI用于实现oop的多态,oop推崇面向接口编程而不是面向实现,面向接口编程可以更好地解耦代码,以达到更高的可扩展性。在多态的情况下,一个接口存在多个实现,实现类通常会向上转型为接口的类型,再通过接口暴漏的api调用实现类的具体方法,即所谓的 “泛化调用”。在语法级别上,RTTI主要应用于三种场景下:一是强制类型转换。二是调用instance of,三是接口泛化调用时。

3.泛化调用某个实现类的某一方法,则必须了解两件事。一是必须识别泛化调用的具体类型,二是必须确定调用的方法的归属,比如一个子类并未重写其抽象父类的某个方法,这时接口泛化调用的是抽象父类的方法,而非子类,就算确定了接口的实现类类型也无济于事。

第一种识别泛化调用的具体类型即是通过RTTI来实现的,Java中用Class对象来实现RTTI,在HotSpot虚拟机中,对象头有一个类型指针,RTTI通过这个指针确定对象的类型信息,从而实现多态。Java中的Class对象与C++不同,是Lazy load的,当创建第一个对类的静态成员引用时,才会加载该类,构造方法也可以视为类的静态方法。

至于如何加载类,则交给ClassLoader,包括BootstrapClassLoader、ExtClassLoader、AppClassLoader以及用户自定义的类加载器,其加载顺序遵循双亲委派/全盘委托,一般自定义类加载器主要是为了资源隔离,典型如Tomcat,自定的ClassLoader用于隔离项目、隔离web容器库与web项目库,在一些场景下如SPI 、JDNI 或interface与implements分别由两个不同的CassLoader加载时,可能会遭遇ClassNotFoundException,可使用ThreadContextClassLoader防止这种情况,Tomcat中的类加载器也是一个道理,并未遵循双亲委派的原则。

第二种即确定泛化调用方法的归属,即方法和NameSpace的绑定方法。java中有两种绑定方式,一是静态绑定,二是动态绑定(运行时绑定)。静态绑定即在编译期就可以确定该方法的NameSpace,比如C中的函数(C仅支持静态绑定),java中的static方法,而动态绑定指在运行时动态地确定方法的NameSpace,编译器根据方法的方法名、方法形参来调用,jvm会维护一个method table,里面保存方法签名和实际方法的映射,父类的方法总是优先级更高,除非子类重写了该方法。关于method teable以及方法调用规则,具体可参考:https://blog.csdn.net/bluetjs/article/details/52608833 。另外像final方法,可以认为是在运行时关闭了动态绑定,从而实现被final修饰的类或方法无法被继承或重写,private同样可以被认为是隐式的final。

4.RTTI是java类型系统的基石,但并不适用于在编译器无法获取类型的情况,这时就需要使用反射。典型的比如com.mysql.jdbc.Driver,在用Maven构建的时候一般指定其Scope为Runtime,避免开发时误引用,使用时一般会直接使用DriverManager的静态方法而无需创建实例,但JDBC规范要求每个数据库厂家必须先像DriverManager注册驱动,才能通过DriverManager获取连接,比如com.mysql.jdbc.Driver里有个静态代码块会调用DriverManager.register(),为了避免创建无意义的Driver对象,一般会通过Class.forName()加载Driver类。反射超乎想象的强大,现在已经是一门高级语言的必备特性了。

 

你可能感兴趣的:(JavaSE)