java中所有对象(object)不是引用类型(reference)就是基本类型(primitive)。不管什么类型对象,java虚拟机都会为之实例化一个 java.lang.Class的不可变实例(Class类的实例就是运行的java应用里的classes和interfaces),这个实例会提供方法来检测对象的成员(members )和类型(type)信息。Class也有能力创建新的classes和objects。最重要的是Class是所有反射(Reflection )APIs的入口。
第一部分:获得类
除了 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 。但这不能用于基础类型。
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对象,并显示它们的名称。