类型信息
1、Class对象
RTTI在Java中工作原理,类型信息在运行时的表示由Class特殊对象完成,它包含了与类有关的信息。
类是程序的一部分,每个类都有一个Class对象。
当程序创建第一个对类的静态成员的引用时,就会加载这个类,这个证明构造器也是类的静态方法。
拥有类型的对象,通过getClass()方法来获得Class引用。
-
Class包含的一些有用方法:
forName():用一个包含目标类全限定类名的String做输入参数,返回一个Class对象的引用。
getName()和getCanonicalName():获取全限定的类名。
getSimpleName():获得不含包名的类名。
isInterface():判断Class对象是否表示某个接口。
getInterfaces():返回Class对象所包含的接口。
getSuperclass():返回直接基类。
newInstance():实现“虚拟构造器”,使用newInstance()来创建的类,必须带有默认构造器。
-
类字面常量(生成Class对象的引用,语法:类名.class)
类字面常量不仅可以应用于普通类,也可应用于接口、数组以及基本数据类型。对于基本数据类型的包装类,还有个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象,如下所示:
使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象。初始化被延迟到对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行。如果一个static final值是“编译期常量”,不需对所属类进行初始化就可以被读取,但只是将一个域设置为static final的,还不足以确保该域是编译期常量。
-
泛化的Class引用
Class引用表示的是它所指向的对象确切类型,而该对象便是Class类的一个对象。
Java SE5之后允许你对Class引用所指向的Class对象的类型进行限定,用到泛型语法。泛型类引用只能赋值为指向其声明的类型,但普通类引用可以被重新赋值为指向任何其他的Class对象。如下所示:
Integer继承自Number,但Integer Class对象不是Number Class对象的子类。
怎么放松使用泛化的Class引用时的限制?使用通配符,它是Java泛型的一部分。通配符“?”,表示“任何事物”。Class<?>优于普通的Class,会产生编译器警告信息。为了创建一个Class引用,它被限定为某种类型,或该类型的任何子类型,需要通配符与extends关键字相结合,创建一个范围。例如:Class<? extends Number> class=int.class;
向Class引用添加泛型的原因仅仅是为了提供编译期类型检查,如果你操作有误,就会有编译器警告信息。使用普通Class引用,如果犯了错误,直到运行时才会发现。
-
新的转型语法
cast():接受参数对象,并将其转型为Class引用的类型。
无法使用普通转型时或存储Class引用希望以后通过这个引用转型时使用。使用情况比较少。
2、类型转换前先做检查
-
RTTI形式包括:
传统的类型转换,由RTTI确保类型转换的正确性,如果执行错误的类型转换,就会抛出ClassCastException异常。
代表对象类型的Class对象。查询Class对象可以获取运行时所需信息。
关键字instanceof。
动态的instanceof:Class中的isInstance()方法提供了一种动态测试对象的途径。
Class中的isAssignableFrom()方法执行运行时的检查,以校验你传递的对象确实属于我们感兴趣的继承结构。
3、注册工厂
工厂对象列表置于一个位于中心的、位置明显的地方,继承结构的基类可能是最佳位置。
4、instanceof与Class的等价性
instanceof和isInstance保持了类型的概念,它指的是“你是这个类吗,或你是这个类的子类吗?”,而如果用==比较的Class对象,就没有考虑继承——它或者是这个确切的类型,或者不是。
5、反射:运行时类型信息
-
应用:
基于构件的编程,在此种编程方式中,将使用某种基于快速应用开发(RAD)的应用构建工具,即集成开发环境(IDE),来构建项目。
希望提供在跨网络的远程平台上创建和运行对象的能力,这被称为远程方法调用(RMI),它允许一个Java程序将对象分布到多台机器上。
Class类与java.lang.reflect类库一起对反射的概念进行支持,该类库包含了Field、Method以及Constructor类(每个类都实现了Member接口)。
RTTI和反射之间的区别:对RTTI来说,编译器在编译时打开和检查.class文件。而对于反射机制来说,.class文件在编译时是不可获取的,是在运行时打开和检查.class文件。
Class的getMethods()和getConstructors()方法分别返回Method对象数组和Constructor对象数组。这两个类都提供了深层方法,用以解析其对象所代表的方法,并获取其名字、输入参数以及返回值。
6、动态代理
代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来替代“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当中间人的角色。
-
java动态代理,动态地创建代理并动态地处理对所代理方法的调用。通过调用静态方法Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)可以创建动态代理,其中:loader,表示类加载器,对于不同来源(系统库或网络等)的类需要不同的类加载器来加载,这是Java安全模型的一部分。可以使用null来使用默认的加载器;interfaces,表示接口或对象的数组,它就是前述代理对象和真实对象都必须共有的父类或者接口;handler,表示调用处理器,它必须是实现了InvocationHandler接口的对象,其作用是定义代理对象中需要执行的具体操作。
7、空对象
空对象,它可以接受传递给它的所代表对象的消息,但是将返回表示为实际上并不存在任何“真实”对象的值。
到处使用空对象并没有任何意义——有时检查null就可,有时可合理地假设不会遇到null,有时甚至可通过NullPointerException来探测异常。
空对象最有用之处在于它更靠近数据,因为对象表示的是问题空间内的实体。例如,许多系统都有一个Person类,而代码中,有很多情况是没有一个实际的人(或者没有某个人的全部信息),我们可使用空对象。即使空对象可以响应“实际”对象可以相应的所有消息,但仍需某种方式去测试其是否为空。要达到此目的,最简单的方式是创建一个标记接口:public interface Null {}
-
通常空对象都是单例的。
空对象的逻辑变体是模拟对象和Stub(存根类),与空对象一样,都表示在最终的程序中所使用“实际”对象。但模拟对象和Stub都只是假扮可传递实际信息的存活对象,而不是像空对象那样可成为null的一种更智能化的替代物。
模拟对象和Stub之间的区别:模拟对象是轻量级和自测试的,通常很多模拟对象被创建出来为了处理各种不同的测试情况。Stub只是返回Stub数据,通常是重量级的,并且常在测试之间被复用,是种复杂对象,做很多事。
8、接口和类型信息
interface关键字的一种重要目标是允许程序员隔离构件,进而降低耦合度。但通过类型信息,这种耦合性还是会被传播出去——接口并非对解耦的一种无懈可击的保障。
反射违反了访问权限的操作,但处理有些问题是必要的。
9、总结
多态机制的方法调用,要求我们拥有基类定义的控制权,因为在你扩展程序时,可能会发现基类并未包含我们想要的方法。如果基类是别人的类或者由别人控制,这时RTTI是一种解决之道:可继承一个新类,然后添加你需要的方法。
为了某个特定类的利益,而将某个特性放进基类里,这意味着从那个基类派生出的所有其他子类都带有这些可能无意义的东西,这会使得接口更不清晰。RTTI可提供一种合理的解决方案,将特定类的的特性放入特定类中。
RTTI可能解决效率问题。
转载于:https://my.oschina.net/90liusq/blog/339776