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

总论:

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.ReflectPermission java.lang.reflect 里的其他类都没有public构造器。为了得到 java.lang.reflect 的其他类(如Method, Array Field ),就得调用 Class 中的合适的方法。依据我们的代码是否可以访问object,类的名字,类型,存在的类 Class (an object, 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[][].class


TYPE Field for Primitive Type Wrappers  
基本类型可以通过 ".class"语法获得相应的类,但也可以通过其包裹类的TYPE属性(field)获得。

Class c = Double.TYPE; // 即  double.class

Class c = Void.TYPE; //  void.class

Methods that Return Classes 
Class.getSuperclass()

Class.getClasses()   返回此类中的公开的classes,interfaces 和enums成员。

Class<?>[] c = Character.class.getClasses();

还有其他的等等。


第二部分:检测类(Class)的修饰符和类型

访问修饰符:public,protected,private
需要重写的修饰符: abstract
限制只能有一个实例的修饰符:static
防止值被修改的修饰符:final
强制严格的浮点行为的修饰符:strictfp
注释 Annotations

java.lang.reflect.Modifier 包含了所有修饰符的声明。也包含了由  Class.getModifiers() 返回的修饰符集合的  解码的方法。

Modifier.toString(c.getModifiers()); // c 是一个类

TypeVariable[] tv = c.getTypeParameters();

Type[] intfs = c.getGenericInterfaces();


打印结果如下:
$ java ClassDeclarationSpy java.util.concurrent.ConcurrentNavigableMap
Class:
  java.util.concurrent.ConcurrentNavigableMap

Modifiers:
  public abstract interface

Type Parameters:
  K V

Implemented 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 API List of members? Inherited members? Private members?
getDeclaredField() no no yes
getField() no yes no
getDeclaredFields() yes no yes
getFields() yes yes no

Class Methods for Locating Methods
Class API List of members? Inherited members? Private members?
getDeclaredMethod() no no yes
getMethod() no yes no
getDeclaredMethods() yes no yes
getMethods() yes yes no

Class Methods for Locating Constructors
Class API List of members? Inherited members? Private members?
getDeclaredConstructor() no N/A1 yes
getConstructor() no N/A1 no
getDeclaredConstructors() yes N/A1 yes
getConstructors() yes N/A1 no

1  Constructors are not inherited.


c.getPackage();

c.getConstructors();

c.getFields();

c.getMethods();


打印结果如下:
$ java ClassSpy java.lang.ClassCastException CONSTRUCTOR
Class:
  java.lang.ClassCastException

Package:
  java.lang

Constructor:
  public java.lang.ClassCastException()
  public java.lang.ClassCastException(java.lang.String)

或者如下:
$ java ClassSpy java.nio.channels.ReadableByteChannel METHOD
Class:
  java.nio.channels.ReadableByteChannel

Package:
  java.nio.channels

Methods:
  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对象,并显示它们的名称。 





  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值