RTTI (run-time Type identification) :
为什么要使用rtti:rtti代表java类型信息,从面向对象的角度来说,每一个对象都是一个类,那么类型也是一个类:类型类,其保证不仅在编译期知道java对象的类型,并且可以在运行期 运用对象的类型信息,在jvm中方法区保存class信息,但是类型对象一般存放在堆中
jvm是如何实现的呢?jvm通过在方法区保存class信息,类型对象一般存放在堆中,具体使用时则是通过堆中的类对象来获取类型信息。
使用场景一:instanceof关键字:尽管对象使用了java多态中的向上转型,依然可以识别出其类型类。
使用场景二: java在运行时识别对象类型有两种情况:传统rtti和反射类是程序的一部分,每一个类都有一个class对象,java使用class对象来执行RTTI
类是程序的一部分,每一个类都有一个class对象,java使用class对象来执行RTTI
Class对象中的forName方法
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
通过源码可以看到Class的静态方法forName 返回一个Class对象,这个方法的副作用是,如果 参数className所指向的类还没有被jvm加载,就先加载该类,执行其中的static方法。
可以不用为了使用该Class引用而持有该类型的对象。
可以看到使用class的forName方法 执行了其中的static方法,而使用.class 获得类型类(类字面常量,下一节会讲),仅把类加载进jvm,而不执行其中的static语句。
类中的static方法,在class对象第一次被加载进jvm时执行,而class对象只在需要时被加载,也就是需要访问类中的静态方法时,因此可以认为构造函数也是class的一个静态方法。
深入理解java虚拟机 中所述,虚拟机把class文件加载到内存,并进行校验,解析和初始化,最后形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制,这些都是在运行期进行的,因此是java具有良好的动态扩展语言特性。
其中规定,遇到new、getstatic、putstatic或invokestatic这四个字节码指令时,如果类没有进行初始化,首先触发其初始化。
加载
加载包括三个步骤:
1. 获取字节流
2. 将字节流所代表的静态存储结构转化为方法区运行时数据结构
3. 内存中生成一个代表这个类的java.lang.Class对象,做为方法区这个类的各种数据的访问入口
* 加载完成后生成Class类的对象,并没有规定必须在堆中,也可以在方法区中,这个对象可以访问方法区中这些类型数据的外部接口,因此也造成多用于反射创建对象。
* 那么什么是方法区呢?
首先需要了解一下java虚拟机的运行时数据区
这里写代码片.cnblogs.com/blog/1182497/201706/1182497-20170616192739978-1176032049.png” alt=”” title=”” />
其中绿色的是每个线程独有的,程序计数器表示字节码的行号指示器
方法区和堆是线程之间共享的。其中堆的作用是存放对象实例,几乎所有的对象实例及数组都要在堆上分配。方法区存储被虚拟机加载的类信息、常量、静态变量等,运行时常量池也是方法区的一部分(Metaspace表示类型的类型,不在jvm内存中,而是在本地内存中)。
类字面常量
也就是上面说的通过 类名.class 获得类型的引用,使用这种方式不会自动的初始化该class对象。
为使用类而做的准备工作包含三个步骤:
1. 加载 加载class,并创建一个class对象
2. 链接 分配静态空间
3. 初始化 执行静态初始化代码,或静态代码块时初始化
如果一个static final 是编译器常量,那么不需要类初始化就可以读取,但是static修饰的不是一个编译器常量(比如不是final修饰的,或者是一个随机变量),那么必须强制进行类初始化。
使用泛型,使 类引用更具体(利用了编译期检查)
instanceof
使用场景:在向下转型前,如果没有其他的信息表明这是一个什么类,那么使用instanceof是必须的,防止castException异常
使用反射可以调用向上转型后子类中的方法
也给程序留下了后门
public class ConcreteRtti implements IRttiInterface {
@Override
public void say() {
System.out.println ("this is IRttiInterface");
}
private void concreteSay(){
System.out.println ("this is concreteSay");
}
public static void main(String[] args) throws Exception {
IRttiInterface iRttiInterface = new ConcreteRtti ();
iRttiInterface.say ();
System.out.println (iRttiInterface.getClass ().getName ());
ConcreteRtti iRttiInterface1 = (ConcreteRtti) iRttiInterface;
iRttiInterface1.concreteSay (); //调用非接口中的方法
Method concreteMethod = iRttiInterface.getClass ().getDeclaredMethod ("concreteSay");
concreteMethod.invoke (iRttiInterface);//调用非接口中的方法
}
}
反射reflect
java reflect包中 包含Construct Field Method等类
反射是在运行时打开和检查.class文件,而普通rtti实在编译器 检查和运行.class,简单的使用
public static void main(String[] args) throws Exception {
Class<ReflectTest> reClass = ReflectTest.class;
ReflectTest test = reClass.newInstance (); //使用newInstance 必须有一个无参构造器
Constructor[] cons = reClass.getConstructors ();
Method[] methods = reClass.getMethods ();
Method method0 = null;
method0 = reClass.getMethod ("say");
assert method0 != null;
method0.invoke (test);
}
使用反射技术包括 动态代理等