Java RTTI and Reflection

Reference:

Java编程思想

java 反射(Reflect)

Java系列笔记(2) - Java RTTI和反射机制

Java Reflection in Action, 有空再补

----------------------------------------------------------------------------------------------

对于面向对象, 以多态的方式编写程序是首选的方式, 即使用基类reference, 而不是具体的类对象来编写程序

在这种情况下, 你根本就不需要知道基类reference具体指向的是那个类对象, 因为编译器会自动调用相应的逻辑, 大大的简化了编程的复杂度

比如下面的例子, 对于draw, 只需要写Shape.draw(), 此时Shape可以指向circle, square, triangle, 任一个对象

所以对于绝大部分情况下, 其实是不需要知道RTTI (Run-time Type Identification)的, 所以当你用到RTTI, 首先确认你真的需要吗, 设计没有问题吗, 不应该使用多态吗?

当然对于一些特殊和复杂的情况下, 获取RTTI是有较大帮助的,

比如上面的例子, 你只想把circle画成红色来高亮强调, 或则在调用旋转操作的时候想要跳过circle(因为旋转对circle没有意义), 这个时候就需要RTTI

对于Java的RTTI, 无论什么形式, 没有什么神秘的, 毫无例外的都是通过class对象来获得信息.

传统的RTTI和新的Reflection的唯一差别, 就是在何时去check class对象文件 
传统的RTTI是在编译时候去check, 这个其实可以满足绝大部分需求

但是对于某些需求, 
从磁盘, 或网络连接中读取一个对象, 在编译的时候还没有读, 所以你无法在编译的时候check 
在RMI(远程方法调用)中, 需要知道你调用的类的各种信息 
这些情况下, class对象文件必须到运行时才能获取(从磁盘或网络上), 这是传统的RTTI无法支持的 
这时就需要使用Reflection接口

 

Class对象

可以看到class对象是RTTI的核心, 下面就具体来看看这个对象

在Java中, 每个class都有一个相应的Class对象(每个类编译完成后, 存放在生成的.class文件中), 用于表示这个类的类型信息. 
Class对象用于创建类的所有普通的instance, 并且RTTI也是通过Class对象实现, 哪怕是最简单的cast

当类第一次被使用时, class对象会被类加载器加载到JVM中,

确切的说, 当第一次引用该类的static member时, class对象会被加载. 构造函数是隐含的static member, 所以用new创建对象的时候也会被当作引用static member 

This happens when the program makes the first reference to a static member of that class. 
It turns out that the constructor is also a static method of a class, even though the static keyword is not used for a constructor. Therefore, creating a new object of that class using the new operator also counts as a reference to a static member of the class.

类加载器, 会先check该class对象是否已经被加载, 如果没有加载, 首先找到对应的.class文件, 然后check这个class文件是否被破坏或含有不良代码, 一旦class对象被载入, 就可以用来创建普通对象

 

Class对象的生成方式

1. 使用Class的静态成员forName

Class.forName("类名字符串") (注意: 类名字符串必须是全称, 包名+类名), 如果找不到该类会抛ClassNotFoundException异常

2. Class literals, 更加安全和高效的方法, 类名.class

Class literals work with regular classes as well as interfaces, arrays, and primitive types.

可用于接口, 数组和基本类型

需要注意的是, 使用Class literals来创建class对象时, 会有惰性, 对象初始化会被延迟到真正使用, 即引用静态成员时发生

对于基本类型, 还可以使用包装器类的Type

 

3.实例对象.getClass()

public class ClassTest {
    public static void main(String [] args)throws Exception{
        String str1="abc";
        Class cls1=str1.getClass();
        Class cls2=String.class;
        Class cls3=Class.forName("java.lang.String");
        System.out.println(cls1==cls2);  //True
        System.out.println(cls1==cls3);  //True
    }
}

 

RTTI的形式

总结一下传统的RTTI的形式, 如下3种

1. 向上转型或向下转型(upcasting and downcasting)

向上转型(子类cast到父类)是绝对安全的, 所以不需要check, 可以通过赋值操作完成 
但是向下转型(父类cast到子类)却是有风险的, 编译器需要check向下转型是否合理, 这就需要使用RTTI来check实际类型

2. Class对象

传统的RTTI和反射都依赖于Class对象, 什么时候是传统RTTI, 什么时候是反射?

Class<?> c = Class.forName(args[0]); //这种情况就是反射, 只有在运行时产能得到args[0]
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors(); 

3. instanceof或isInstance()

从下面看出两种的不同用法,

//x是否是Dog类的instance
x instanceof Dog  // obj instanceof classname
Dog.class.isInstance(x) //动态的instanceof, classobj.isInstance(obj)

 

Reflection, 反射

反射, 即在Java运行时环境中动态获取类的信息, 以及动态调用对象的方法的功能, 让Java跨入半动态语言的行列, 毕竟Java不允许动态的更改.

Java 反射机制主要提供了以下功能:

- 在运行时判断任意一个对象所属的类

- 在运行时构造任意一个类的对象

- 在运行时判断任意一个类所具有的成员变量和方法

- 在运行时调用任意一个对象的方法

- 生成动态代理

 

在JDK中, 主要由以下类来实现Java 反射机制, 这些类都位于java.lang.reflect包中:

- Java.lang.Class类: 代表一个类

- Field类: 代表类的成员变量(成员变量也称为类的属性)

- Method类: 代表类的方法

- Constructor类: 代表类的构造方法

- Array类: 提供了动态创建数组 以及访问数组元素的静态方法

使用的例子, 参考java 反射(Reflect)

 

动态代理

普通的代理, 静态代理, 很简单, 问题就是必须手工的写代理 
那么如果真实的对象中有100个方法, 那么在代理类中就需要写100个代理接口, 是不是很麻烦

动态代理, 就是可以自动生成代理类, 其实代理类是很简单的, 实现代理类的关键就是知道真正的对象中有哪些接口, Reflection出场... 
Reflection包中封装实现了动态代理, 可以直接使用

下面给出两种创建动态代理的方法 
一种, 先生成代理类, 再创建代理对象 
另一种, 直接生成代理对象

/**** 方式一****/
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//创建动态代理类
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
//创建动态代理类的实例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

/**** 方式二****/
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//直接创建动态代理类的实例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);

本文章摘自博客园,原文发布日期:2013-11-18
时间: 2024-08-17 13:55:23

Java RTTI and Reflection的相关文章

java rtti学习总结

利用RTTI可根据一个匿名的基础类句柄调查出类型信息.但正是由于这个原因,新手们极易误用它,因为有些时候多形性方法便足够了.对那些以前习惯程序化编程的人来说,极易将他们的程序组织成一系列switch语句.他们可能用RTTI做到这一点,从而在代码开发和维护中损失多形性技术的重要价值.Java的要求是让我们尽可能地采用多形性,只有在极特别的情况下才使用RTTI. 但为了利用多形性,要求我们拥有对基础类定义的控制权,因为有些时候在程序范围之内,可能发现基础类并未包括我们想要的方法.若基础类来自一个库,

Java中利用Reflection API优化代码

摘要 开发者通过各种各样的方法来尝试避免单调冗余的编程.一些编程的规则例如继承.多态或者设计模型可以帮助开发者避免产生多余的代码.不过由于软件开发方面存在着不确定性,因此这些规则并不能消除代码维护和重新编写的需要.在很多时候维护都是不可避免的,只有不能运作的软件才是从不需要维护的.不过,这篇文章介绍了你可以使用Java的Reflection API的功能来减少单调的代码编写,并可以使用活动的代码产生来克服reflection的限制. 数据配置(由外部的源头得到数据并且将它装载到一个Java对象中

利用Java反射(Reflection) 机制提高代码的行覆盖率

在本文中,您将看到如何通过使用反射机制,在外部直接对目标类中的不可访问成员进行测试,以提高被测代码数量:以及通过修改 Cobertura 源码,使其支持通过正则表达式来过滤不需要进行单元测试的代码,以降低代码总数.代码覆盖率的提高,减少了单元测试过程中未被覆盖的代码数量,降低了http://www.aliyun.com/zixun/aggregation/7155.html">开发人员编写或修改单元测试用例的时间成本,从而提高了整个单元测试的效率. 单元测试是软件开发过程中重要的质量保证环

java的rtti机制语法

Java用Class对象实现自己的RTTI功能--即便我们要做的只是象造型那样的一些工作.Class类也提供了其他大量方式,以方便我们使用RTTI. 首先必须获得指向适当Class对象的的一个句柄.就象前例演示的那样,一个办法是用一个字串以及Class.forName()方法.这是非常方便的,因为不需要那种类型的一个对象来获取Class句柄.然而,对于自己感兴趣的类型,如果已有了它的一个对象,那么为了取得Class句柄,可调用属于Object根类一部分的一个方法:getClass().它的作用是

rtti在java造型前的检查

迄今为止,我们已知的RTTI形式包括: (1) 经典造型,如"(Shape)",它用RTTI确保造型的正确性,并在遇到一个失败的造型后产生一个ClassCastException违例. (2) 代表对象类型的Class对象.可查询Class对象,获取有用的运行期资料. 在C++中,经典的"(Shape)"造型并不执行RTTI.它只是简单地告诉编译器将对象当作新类型处理.而Java要执行类型检查,这通常叫作"类型安全"的下溯造型.之所以叫"

java对RTTI的需要

请考虑下面这个熟悉的类结构例子,它利用了多形性.常规类型是Shape类,而特别衍生出来的类型是Circle,Square和Triangle. 这是一个典型的类结构示意图,基础类位于顶部,衍生类向下延展.面向对象编程的基本目标是用大量代码控制基础类型(这里是Shape)的句柄,所以假如决定添加一个新类(比如Rhomboid,从Shape衍生),从而对程序进行扩展,那么不会影响到原来的代码.在这个例子中,Shape接口中的动态绑定方法是draw(),所以客户程序员要做的是通过一个普通Shape句柄调

一个java类方法提取器

很少需要直接使用反射工具:之所以在语言中提供它们,仅仅是为了支持其他Java特性,比如对象序列化(第10章介绍).Java Beans以及RMI(本章后面介绍).但是,我们许多时候仍然需要动态提取与一个类有关的资料.其中特别有用的工具便是一个类方法提取器.正如前面指出的那样,若检视类定义源码或者联机文档,只能看到在那个类定义中被定义或覆盖的方法,基础类那里还有大量资料拿不到.幸运的是,"反射"做到了这一点,可用它写一个简单的工具,令其自动展示整个接口.下面便是具体的程序:   //:

Java反序列化漏洞利用的学习与实践

本文讲的是Java反序列化漏洞利用的学习与实践, 利用DeserLab 建议你在阅读本文之前,先阅读<攻击Java反序列化过程>,这样你就会对java反序列化有一个比较清晰的认识.除此之外,这篇文章还提到了一个"DeserLab"的演示应用.为了有效利用反序列化漏洞,理解序列化的原理以及反序列化漏洞利用的工作原理(如面向属性的编程原理),研究人员就要找到一个可供模拟的实验环境,而"DeserLab"就是一个这样的环境. 要想利用一个漏洞,通常的方法首先是

TIJ阅读笔记(第十章)

笔记 10:检测类型  运行时类型识别(run-time type identification,缩写为RTTI). 为什么会需要RTTI  collection是一种工具,它只有一种用途,就是要为你保管其它对象.因此出于通用性的考虑,这些collection应该能持有任何东西.所以它们持有Object.    Class对象    想要知道JAVA的RTTI是如何工作的,你就必须首先知道程序运行的时候,类型信息是怎样表示的.这是由一种特殊的,保存类的信息的,叫做"Class对象(Class o