Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
Class
中提供了两类方法,用于访问字段,方法和构造函数:枚举这些成员的方法和搜索特定成员的方法。还有一些不同的方法可以访问直接在类上声明的成员,而不搜索超接口和超类的继承成员。下表提供了所有成员定位方法及其特征的摘要。
Class API |
成员列表? | 继承的成员? | 私有成员? |
---|---|---|---|
getDeclaredField() |
no | no | yes |
getField() |
no | yes | no |
getDeclaredFields() |
yes | no | yes |
getFields() |
yes | yes | no |
Class API |
成员列表? | 继承的成员? | 私有成员? |
---|---|---|---|
getDeclaredMethod() |
no | no | yes |
getMethod() |
no | yes | no |
getDeclaredMethods() |
yes | no | yes |
getMethods() |
yes | yes | no |
Class API |
成员列表? | 继承的成员? | 私有成员? |
---|---|---|---|
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
和其他超类中定义的异常链接机制构造函数(具有 Throwable
参数)没有找到。
$ 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()
。此路径的后续部分 Examining Enums 中的
示例包含可能的实现。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()); }