您的位置:首页 > 编程语言 > Java开发

java 反射(Reflection)和内省(Introspector)

2013-10-21 21:45 447 查看
总论:java中所有对象(object)不是引用类型(reference)就是基本类型(primitive)。不管什么类型对象,java虚拟机都会为之实例化一个 java.lang.Class的不可变实例(Class类的实例就是运行的java应用里的classes和interfaces),这个实例会提供方法来检测对象的成员(members )和类型(type)信息。Class也有能力创建新的classes和objects。最重要的是Class是所有反射(Reflection )APIs的入口。tip:java.lang.Class<T>继承自java.lang.Object第一部分:获得类除了java.lang.reflect.ReflectPermissionjava.lang.reflect里的其他类都没有public构造器。为了得到java.lang.reflect的其他类(如Method,ArrayField),就得调用Class中的合适的方法。依据我们的代码是否可以访问object,类的名字,类型,存在的类Class(anobject, the name of class, a type, or an existing Class),我们有几种得到Class的方式。下面依次介绍:Object.getClass() 这种方式只对引用类型有效。1. Class c = "a string".getClass();// 返回String的类。2.import java.util.HashSet;import java.util.Set;Set<String> s = new HashSet<String>(); Class c = s.getClass();// 返回java.util.HashSet类。The .class Syntax 如果无法获得实例但是可以获得类型,我们就可以通过在Class后面加".class"来得到此类的Class。这也是基础类型(primitive type)获得相应Class最容易的方式。boolean b;Class c = b.getClass(); // 编译时错误 Class c = boolean.class;  // 正确Class c = int[][][].class;// 正确Class.forName() 如果知道一个类的完整路径,那么也可以通过静态方法Class.forName()获得相应的Class。但这不能用于基础类型。Class c = Class.forName("com.duke.MyLocaleServiceProvider");Class cDoubleArray = Class.forName("[D"); // 返回的类型与 double[].class 一致。Class cStringArray = Class.forName("[[Ljava.lang.String;"); // String[][].classTYPE Field for Primitive Type Wrappers 基本类型可以通过".class"语法获得相应的类,但也可以通过其包裹类的TYPE属性(field)获得。Class c = Double.TYPE; // 即 double.classClass c = Void.TYPE; // void.classMethods that Return Classes Class.getSuperclass()Class.getClasses()  返回此类中的公开的classes,interfaces 和enums成员。Class<?>[] c = Character.class.getClasses();还有其他的等等。第二部分:检测类(Class)的修饰符和类型访问修饰符:public,protected,private需要重写的修饰符: abstract限制只能有一个实例的修饰符:static防止值被修改的修饰符:final强制严格的浮点行为的修饰符:strictfp注释Annotationsjava.lang.reflect.Modifier包含了所有修饰符的声明。也包含了由 Class.getModifiers()返回的修饰符集合的 解码的方法。Modifier.toString(c.getModifiers()); // c 是一个类TypeVariable[] tv = c.getTypeParameters();Type[] intfs = c.getGenericInterfaces();打印结果如下:$ java ClassDeclarationSpy java.util.concurrent.ConcurrentNavigableMapClass:  java.util.concurrent.ConcurrentNavigableMapModifiers:  public abstract interfaceType Parameters:  K VImplemented Interfaces:  java.util.concurrent.ConcurrentMap<K, V>  java.util.NavigableMap<K, V>Inheritance Path:  -- No Super Classes --Annotations:   -- No Annotations --第三部分:发现类成员(Members)Class 提供的访问fields,methods和constructors的方法(methods)可以分成两种类型:枚举所有成员的方法(methods)和寻找特殊成员的方法(methods)。Class Methods for Locating Fields
Class APIList of members?Inherited members?Private members?
getDeclaredField()nonoyes
getField()noyesno
getDeclaredFields()yesnoyes
getFields()yesyesno
Class Methods for Locating Methods
Class APIList of members?Inherited members?Private members?
getDeclaredMethod()nonoyes
getMethod()noyesno
getDeclaredMethods()yesnoyes
getMethods()yesyesno
Class Methods for Locating Constructors
Class APIList of members?Inherited members?Private members?
getDeclaredConstructor()noN/A1yes
getConstructor()noN/A1no
getDeclaredConstructors()yesN/A1yes
getConstructors()yesN/A1no
1 Constructors are not inherited.c.getPackage();c.getConstructors();c.getFields();c.getMethods();打印结果如下:$ java ClassSpy java.lang.ClassCastException CONSTRUCTORClass:  java.lang.ClassCastExceptionPackage:  java.langConstructor:  public java.lang.ClassCastException()  public java.lang.ClassCastException(java.lang.String)或者如下:$ java ClassSpy java.nio.channels.ReadableByteChannel METHODClass:  java.nio.channels.ReadableByteChannelPackage:  java.nio.channelsMethods:  public abstract int java.nio.channels.ReadableByteChannel.read    (java.nio.ByteBuffer) throws java.io.IOException  public abstract void java.nio.channels.Channel.close() throws    java.io.IOException  public abstract boolean java.nio.channels.Channel.isOpen()《Think in java》中写道“Class 类支持反射的概念,Java附带的库java.lang.reflect包含了Field,Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行期创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的属性,用invoke()方法调用与Method对象关联的方法。另外,你还可以调用getFields(),getMethods(),getConstructors()等等很便利的方法,以返回表示属性、方法以及构造器的对象数组,这些对象(在JDK文档中,可找到与Class类相关的更多的资料)。这样,匿名对象的类信息就能在运行期被完全确定下来,而在编译期不需要知道任何事情。重要的是,反射机制并没有什么魔法。当你通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(就象RTTI那样)。但在这之后,在做其它事情之前,必须加载那个类的Class对象。因此,那个类的.class文件对于JVM来说必须是可获取的,要么在本地机器上,要么可以通过网络取得。所以RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译期打开和检查.class文件。(换句话说,我们可以用“普通”方式调用一个对象的所有方法。)而对于反射机制来说.class文件在编译期间是不可获取的,所以是在运行期打开和检查.class文件。 ”应用:使用内省器(Introspector)来抽取出BeanInfo JavaBean模型最关键部分之一,发生在当你从选用区拖动一个Bean,然后把它放置到窗体上的时候。应用程序构建工具必须能够创建这个Bean(如果有缺省构造器就可以创建),然后在不访问Bean的源代码的情况下,抽取出所有必要信息,以创建属性和事件处理器的列表。部分解决方案在第 10 章就出现了:Java的反射机制能发现未知类的所有方法。对于解决JavaBean的这个问题,这是个完美的方案,你不用像其它可视化编程语言那样使用任何语言附加的关键字。实际上,Java语言里加入反射机制的主要原因之一就是为了支持JavaBean(尽管反射也支持对象序列化和远程方法调用)。所以,你也许会认为应用程序构建工具的编写者将使用反射来抽取Bean的方法,然后在方法里面查找出Bean的属性和事件。这当然是可行的,不过Java的设计者希望提供一个标准工具,不仅要使Bean用起来简单,而且对于创建更复杂的Bean,能够提供一个标准方法。这个工具就是Introspector类,这个类最重要的就是静态的getBeanInfo( )方法。你向这个方法传递一个Class对象引用,它能够完全侦测这个类,然后返回一个BeanInfo对象,你可以通过这个对象得到Bean的属性、方法和事件。 代码片段:
BeanInfo bi = null;
bi = Introspector.getBeanInfo(bean, Object.class);
PropertyDescriptor[] properties =
bi.getPropertyDescriptors();
for(int i = 0; i < properties.length; i++) {
Class p = properties[i].getPropertyType();
if(p == null) continue;
print("Property type:\n  " + p.getName() +
"Property name:\n  " + properties[i].getName());
Method readMethod = properties[i].getReadMethod();
if(readMethod != null)
print("Read method:\n  " + readMethod);
Method writeMethod = properties[i].getWriteMethod();
if(writeMethod != null)
print("Write method:\n  " + writeMethod);
print("====================");
}
print("Public methods:");
MethodDescriptor[] methods = bi.getMethodDescriptors();
for(int i = 0; i < methods.length; i++)
print(methods[i].getMethod().toString());
print("======================");
打印结果:
class name: Frog
Property type:
Color
Property name:
color
Read method:
public Color getColor()
Write method:
public void setColor(Color)
====================
Property type:
Spots
Property name:
spots
Read method:
public Spots getSpots()
Write method:
public void setSpots(Spots)
====================
创建一个BeanInfo对象,成功的话,就调用BeanInfo的方法得到有关其属性、方法和事件的信息。你会发现Introspector.getBeanInfo()方法有第二个参数,它用来告诉Introspector在哪个继承层次上停止查询。因为我们不关心来自Object的方法,所以这里的参数让Introspector在解析来自Object的所有方法前停止查询。对于属性来说,getPropertyDescriptors( )返回类型为PropertyDescriptor的数组,你可以针对每一个PropertyDescriptor都调用getPropertyType()来得到“通过属性方法设置和返回的对象”类型。然后,针对每个属性,你可以通过getName( )方法得到它的别名(从方法名中抽取),通过getReadMethod( )方法得到读方法,通过getWriteMethod()方法得到写方法。后两个方法返回Method对象,它们能够用来在对象上调用相应的方法(这是反射的一部分)。对于公共方法(包括属性方法),getMethodDescriptors( )方法返回类型为MethodDescriptor的数组。对于数组的每个元素,你可以得到相关联的Method对象,并显示它们的名称。 
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息