前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java--运行期类型鉴定(RTTI)

Java--运行期类型鉴定(RTTI)

作者头像
SuperHeroes
发布2018-05-22 16:09:31
6260
发布2018-05-22 16:09:31
举报
文章被收录于专栏:云霄雨霁云霄雨霁

RTTI的使用场景:

例如,类的关系如上图所示,我们可以像下面代码一样将它们放进一个Vector中:

代码语言:javascript
复制
public static void main(String[] args) {
    Vector s = new Vector();
    s.addElement(new Circle());
    s.addElement(new Square());
    s.addElement(new Triangle());
    Enumeration e = s.elements();
    while(e.hasMoreElements())
        ((Shape)e.nextElement()).draw();
}

因为Vector中存放的都是Object句柄,所以取出后进行下溯造型为Shape.。这是 RTTI 最基本的形式,因为在 Java 中,所有造型都会在运行期间得到检查,以确保其正确性。那正是RTTI 的意义所在:在运行期,对象的类型会得到鉴定。 

但我们也只能下溯到Shape, 并不能得出它到底是哪一种图形。如果我们需要找出所有的圆并填充蓝色,就要使用RTTI技术用它查找某个Shape句柄到底是什么类型。

Class对象:

理解RTTI 的工作原理要先了解Class对象。 Class 对象是用来创建属于某个类的全部“常规”或“普通”对象。 每个类都有一个对应的 Class对象。换言之,每次写一个新类时,同时也会创建一个Class 对象(更恰当地说,是保存在一个完全同名的.class 文件中)。在运行期,一旦我们想生成哪个类的一个对象,Java 虚拟机(JVM)首先就会检查那个类型的Class对象是否已经载入。若未载入,JVM就会查找同名的.class 文件并将其载入。所以Java 程序启动时并不是完全载入的,这一点与许多传统语言都不同。 一旦那个类型的Class 对象进入内存,就用它创建那一类型的所有对象。 

为获得 Class的一个句柄,一个办法是使用forName()。它的作用是取得包含了目标类文本名字的一个 String(注意拼写和大小写)。最后返回的是一个Class 句柄。 第二种方式是使用“类标记”。举例说明:

代码语言:javascript
复制
class Candy {
    static { System.out.println("Loading Candy"); }
}
class Gum {
    static { System.out.println("Loading Gum"); }
}
class Cookie {
    static { System.out.println("Loading Cookie"); }
}
public class SweetShop {
    public static void main(String[] args) {
        new Candy();
        System.out.println("After creating Candy");
        Class.forName("Gum");    //使用forName()
        System.out.println( "After Class.forName(\"Gum\")");
        Cookie.class;            //使用类标记
        System.out.println("After Cookie.class");
    }
}

对每个类来说(Candy,Gum 和Cookie),都有一个 static从句,用于在类首次载入时执行。相应的信息会打印出来,告诉我们载入是什么时候进行的。最后打印结果为:

Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After Cookie.class

  1. Loading Candy
  2. After creating Candy
  3. Loading Gum
  4. After Class.forName("Gum")
  5. Loading Cookie
  6. After Cookie.class

更推荐使用类标记,因为它会在编译期得到检查并且因为取消了对方法的调用,效率更高。

类标记不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。除此以外,针对每种基本数据 类型的封装器类,它还存在一个名为TYPE 的标准字段。TYPE 字段的作用是为相关的基本数据类型产生 Class 对象的一个句柄,如下所示:

造型前的检查:

  1. 静态检查:instanceof关键字
  2. 动态检查:isInstance()方法

关键字instanceof告诉我们对象是不是一个特定类型的实例。它会返回一个布尔值:

代码语言:javascript
复制
if(x instanceof Dog) 
    ((Dog)x).bark();

在Java 1.0 中,对 instanceof 有一个比较小的限制:只可将其与一个已命名的类型比较,不能同Class对象作对比。假如一个集合中有好几种类型,我们需要统计每种类型出现的次数,那么我们需要为每一种类型写一个if(x instanceof 类型x),这样非常麻烦而且注意,集合中的类型我们必须已知。

Java 1.1 为Class 类添加了 isInstance()方法。利用它可以动态调用instanceof 运算符。

代码语言:javascript
复制
Object o = pets.elementAt(i);    //待判断的对象
    for (int j = 0; j < petTypes.length; ++j)    //petTypes[]中是所有可能的类型
        if (petTypes[j].isInstance(o)) {
            String key = petTypes[j].toString();
            ((Counter)h.get(key)).i++;
        }

RTTI语法:

Java 用Class对象实现自己的RTTI 功能。Class类也提供了 其他大量方式,以方便我们使用RTTI。

除了上面提到的forName()方法外,Class类还有以下几个方法比较常用:

  • Class.getInterfaces方法会返回Class对象的一个数组,用于表示包含在Class对象内的接口。
  • Class.getSuperclass()查询该对象的直接基础类是什么。
  • Class.newInstance()方法似乎是克隆(clone())一个对象的另一种手段。但两者是有区别的。利用newInstance(),我们可在没有现成对象供“克隆”的情况下新建一个对象。

用 newInstance()创建的类必须有一个默认构建器。 没有办法用 newInstance()创建拥有非默认构建器的对象。

代码语言:javascript
复制
interface HasBatteries {}
interface Waterproof {}
interface ShootsThings {}
class Toy {
    Toy() {}
    Toy(int i) {}
}
class FancyToy extends Toy implements HasBatteries, Waterproof, ShootsThings {
    FancyToy() { super(1); }
}
public class ToyTest {
    public static void main(String[] args) {
        Class c = null;    //class句柄
        try {
            c = Class.forName("FancyToy");
        } catch(ClassNotFoundException e) {}
        printInfo(c);    //输出:“Class name: FancyToy is interface? [false]”
        Class[] faces = c.getInterfaces();    //获得实现的接口
        for(int i = 0; i < faces.length; i++)
            printInfo(faces[i]); 
            //输出:“Class name: HasBatteries is interface? [true]”
            //     “Class name: Waterproof is interface? [true]”
            //     “Class name: ShootsThings is interface? [true]”
        Class cy = c.getSuperclass();     //获得基础类
        Object o = null;
        try {
            o = cy.newInstance(); // 用cy新建一个对象
        } catch(InstantiationException e) {}
          catch(IllegalAccessException e) {}
        printInfo(o.getClass());    //输出:“Class name: Toy is interface? [false] ”
    }
    //打印--通过getName()获得Class句柄的名字,并用interface()调查它是不是一个接口。  
    static void printInfo(Class cc) {
       System.out.println("Class name: " + cc.getName() " is interface? [" cc.isInterface()+ "]");
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档