Class类中提供了两类方法访问类中的字段、方法和构造方法:枚举类中这些成员的方法和搜索类中特定成员的方法。同时有特别的方法获取在类中直接声明的成员,方法搜索继承的类的父类(superclass)和父接口(superinterface)。下面几个表格总结了这些获得类成员的方法。
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 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 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 构造方法不能继承。
给定一个类名和想要探查的成员,ClassSpy这个例子使用 get*s()方法将所有的公共元素包括继承来的元素都显示在列表里
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Member; import static java.lang.System.out; enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL } public class ClassSpy { public static void main(String... args) { try { Class<?> c = Class.forName(args[0]); out.format("Class:%n %s%n%n", c.getCanonicalName()); Package p = c.getPackage(); out.format("Package:%n %s%n%n", (p != null ? p.getName() : "-- No Package --")); for (int i = 1; i < args.length; i++) { switch (ClassMember.valueOf(args[i])) { case CONSTRUCTOR: printMembers(c.getConstructors(), "Constructor"); break; case FIELD: printMembers(c.getFields(), "Fields"); break; case METHOD: printMembers(c.getMethods(), "Methods"); break; case CLASS: printClasses(c); break; case ALL: printMembers(c.getConstructors(), "Constuctors"); printMembers(c.getFields(), "Fields"); printMembers(c.getMethods(), "Methods"); printClasses(c); break; default: assert false; } } // production code should handle these exceptions more gracefully } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static void printMembers(Member[] mbrs, String s) { out.format("%s:%n", s); for (Member mbr : mbrs) { if (mbr instanceof Field) out.format(" %s%n", ((Field)mbr).toGenericString()); else if (mbr instanceof Constructor) out.format(" %s%n", ((Constructor)mbr).toGenericString()); else if (mbr instanceof Method) out.format(" %s%n", ((Method)mbr).toGenericString()); } if (mbrs.length == 0) out.format(" -- No %s --%n", s); out.format("%n"); } private static void printClasses(Class<?> c) { out.format("Classes:%n"); Class<?>[] clss = c.getClasses(); for (Class<?> cls : clss) out.format(" %s%n", cls.getCanonicalName()); if (clss.length == 0) out.format(" -- No member interfaces, classes, or enums --%n"); out.format("%n"); } }
这个例子是比较短小的,然而printMembers()方法却十分笨重因为java.lang.reflect.Member接口自从最早版本的反射实现中就已经存在,所以当泛型出现时却不能对其进行修改使其包含更有用的getGenericString()方法。唯一的选择就是像例子里那样逐个测试并根据实际类型调用printConstructors()、printFields()和printMethods()方法,或者只满足于得到Member.getName()输出的有限的结果。
例子的输出和解释如下,用户输入用斜体标出。
$ 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)
由于构造方法是不能继承的,所以在父类RuntimeException中定义的异常链机制的构造方法以及其他父类都没有找到。
$ 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()
接口java.nio.channels.ReadableByteChannel中定义了read()方法。其他方法是从父接口继承来的。这段示例代码可以很容易被修改为只列出在这个类中定义的方法,只要把get*s()换成getDeclared*s()就可以了。
$ java ClassSpy ClassMember FIELD METHOD Class: ClassMember Package: -- No Package -- Fields: public static final ClassMember ClassMember.CONSTRUCTOR public static final ClassMember ClassMember.FIELD public static final ClassMember ClassMember.METHOD public static final ClassMember ClassMember.CLASS public static final ClassMember ClassMember.ALL Methods: public static ClassMember ClassMember.valueOf(java.lang.String) public static ClassMember[] ClassMember.values() public final int java.lang.Enum.hashCode() public final int java.lang.Enum.compareTo(E) public int java.lang.Enum.compareTo(java.lang.Object) public final java.lang.String java.lang.Enum.name() public final boolean java.lang.Enum.equals(java.lang.Object) public java.lang.String java.lang.Enum.toString() public static <T> T java.lang.Enum.valueOf (java.lang.Class<T>,java.lang.String) public final java.lang.Class<E> java.lang.Enum.getDeclaringClass() public final int java.lang.Enum.ordinal() public final native java.lang.Class<?> java.lang.Object.getClass() public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final void java.lang.Object.wait() hrows java.lang.InterruptedException public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
在结果中的字段部分,枚举类型常量也被列出了。从技术角度讲他们也是字段,但还是应该与其他字段有所区别。可以用java.lang.reflect.Field.isEnumConstant()方法达到这个目的。后续章节(检查枚举类型)中的EnumSpy这个例子展示了一种可能的实现方式。
在输出中的方法部分,方法名包括了所在类名。所以可以看到toString()方法是由Enum类实现而不是从Object类继承来的。可以对代码做一些改进使用Field.getDeclaringClass()方法使这一点更加直观。下面的一段代码展示了一种可能的方式。
if (mbr instanceof Field) { Field f = (Field)mbr; out.format(" %s%n", f.toGenericString()); out.format(" -- declared in: %s%n", f.getDeclaringClass()); }