zoukankan      html  css  js  c++  java
  • RTTI

    RTTI 翻译过来是运行时类型信息。一个引用不仅可以指向和自己类型一致的对象,还可以指向自己子类的对象。那么JVM在执行代码时是如何判定引用指向的对象是否合法?这时就需要用到RTTI。

    一个小案例

    
    class Base {}
    
    class Foo {}
    
    public class Test{
    	public static void main(String[] args) {
    		Base base = new Base();
    		Object obj = base;
    		Foo foo = (Foo) obj;    //运行时抛异常
    	}
    }
    

    上的代码编译时会通过,但是运行时会抛出ClassCastException异常。那么问题来了,编译时为啥通过了?JVM虚拟机咋就是知道强转出现问题了?首先分析代码

    Base base = new Base();
    Object obj = base;
    Foo foo = (Foo) obj;
    

    第一行创建了一个Base对象,定义一个Base引用,将该引用指向创建的对象。这行代码是没有什么问题,因为引用的类型和对象的类型是一致的。

    第二行代码将Base型引用赋值给一个Object引用,实质是让obj指向base指向的对象。这是向上转型,没有问题,当然编译器会对向上转型做检查的。

    第三行代码就出现问题了,它实质是让一个Foo类型引用指向了一个Base对象,这是不合理的。因为从类的声明上来看这个两个类半毛钱关系都没有。

    分析完毕后我们来分析一下为啥编译能通过。首先一行是没有异议的。第二行是编译器会查看类的继承,它发现base是Base类型,而obj是Object类型,从继承角度来讲将子类引用赋值给父类引用是没有问题的。第三行代码对编译器来说也是没有问题了,因为无论是向上还是向下转型,编译器只看继承关系,如果存在继承关系那么编译器会放过的,Object是所有类的基类,所以没有毛病。编译器其实只能做一些简单的类型检查,即它只判断赋值号左边的类型和右边类型是否一致或存在某种关系,它不能透过现象看本质。

    JVM是如何知道转型出现了问题了?它查看了obj的RTTI(通俗的讲它查看了obj指向对象的类型)发现和foo变量的类型不一致,所以它认为强转出现了问题,实际上我们不能将Foo引用指向Base对象。

    如何查看RTTI

    查看一个引用的RTTI和class对象密不可分,这个对象是一个特殊对象,和类同生共死。class对象中包含了类的所有信息,比如类名。当我们编译一个类后,编译器会生成相应的class对象,并将该类和class对象一同写到了.class文件中。当类加载器加载类时会连同class对象一同加载到内存。而当我们创建对象时,JVM虚拟机是根据class对象创建出普通对象。因此,普通对象中将会持有class对象的引用。现在事情就很简单了,JVM通过引用找到普通对象,通过普通对象中的引用找到class对象,通过class对象查到了类信息,这时一个引用的RTTI就被获得了。

    获取RTTI

    RTTI用于描述class对象信息,Java中也为程序员提供了获取RTTI的接口

    Object obj = new Base();
    Class<?> c = obj.getClass();  //返回class对象引用
    

    上面的代码我们获取了class对象,而obj的RTTI就在c中

    前面提到对象都是通过class对象创建出来的,因此我们可以采用别的方式来创建对象,下面是一个小例子:

    Class<?> baseClass = Class.forName("Base"); //返回class对象引用
    Base base = baseClass.newInstance();
    

    RTTI的表现形式

    以下行为会查看RTTI:

    • 向下转型,A a = (B) b;
    • 获取Class对象,Class<?> c = a.getClass();
    • 对于关键字instanceof的使用,instanceof主要用来查看一个对象是否属于某个类

    反射

    要讲反射首先要将RTTI和反射的应用场景搞清楚。反射和RTTI是没有直接关系的,只不过因为反射也涉及到类型信息才放到这里讲。
    RTTI的应用场景是这样的:给一个引用,然后识别这个引用的类型信息,它需要建立在类已知的基础上,即编译器见过这个类,那么JVM或者在我们代码中才能获取一个引用的RTTI。

    通过反射也能获取类型信息,它是在我们不知道类信息的基础上来做的。比如某个类没有在本地存储,而是在将来的某个时刻会通过网络发送过来。那么我们在代码中无法通过new的方式来创建这个类的对象,也不能调用其方法(如果这样做,编译器会报错说找不到该类),这时就需要用到反射了。

    下面举一个简单的例子来查看类信息

    package xdysite.cn;
    
    import java.lang.reflect.Method;
    
    class Base {
    	public void b() {
    		System.out.println("It is Base class");
    	}
    }
    
    public class Test{
    	public static void main(String[] args) {
    		try {
    			//获取class对象
    			Class<?> c = Class.forName("xdysite.cn.Base");
    			//获取内部方法b
    			Method m = c.getMethod("b");
    			//创建普通对象
    			Object obj = c.newInstance();
    			//调用该方法
    			m.invoke(obj);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    注意:虽然Base和Test在同一个包内,但是要调用Base中的方法,其权限必须是public,否则会报找不到该方法

    http://www.cnblogs.com/lzq198754/p/5780331.html

    小结

    RTTI和反射都是获取类型信息,但是两个的应用场景是不一致的,所以在理解的时候要分清楚这一点

  • 相关阅读:
    SmartDb代码修改
    windows下Nginx+RTMP部署
    嵌入式linux下获取flash分区大小
    (转)Qt添加windows开机自启动
    (转)交叉编译lrzsz
    关于海思SDK在Ubuntu下安装错误问题
    电总协议串口调试助手
    使用git将本地仓库上传到远程仓库(转)
    c++中包含string成员的结构体拷贝导致的double free问题
    59. 可变参数
  • 原文地址:https://www.cnblogs.com/xidongyu/p/6961824.html
Copyright © 2011-2022 走看看