# 发现类成员

> 原文： [https://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html](https://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html)

[`Class`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html) 中提供了两类方法，用于访问字段，方法和构造函数：枚举这些成员的方法和搜索特定成员的方法。还有一些不同的方法可以访问直接在类上声明的成员，而不是用于搜索继承成员的超接口和超类的方法。下表提供了所有成员定位方法及其特征的摘要。

<caption>Class Methods for Locating Fields</caption>
| [`Class`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html) API | 会员名单？ | 继承成员？ | 私人会员？ |
| --- | --- | --- | --- |
| [`getDeclaredField()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredField-java.lang.String-) | 没有 | 没有 | 是 |
| [`getField()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getField-java.lang.String-) | 没有 | 是 | 没有 |
| [`getDeclaredFields()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredFields--) | 是 | 没有 | 是 |
| [`getFields()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getFields--) | 是 | 是 | 没有 |

<caption>Class Methods for Locating Methods</caption>
| [`Class`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html) API | 会员名单？ | 继承成员？ | 私人会员？ |
| --- | --- | --- | --- |
| [`getDeclaredMethod()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredMethod-java.lang.String-java.lang.Class...-) | 没有 | 没有 | 是 |
| [`getMethod()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getMethod-java.lang.String-java.lang.Class...-) | 没有 | 是 | 没有 |
| [`getDeclaredMethods()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredMethods--) | 是 | 没有 | 是 |
| [`getMethods()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getMethods--) | 是 | 是 | 没有 |

<caption>Class Methods for Locating Constructors</caption>
| [`Class`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html) API | 会员名单？ | 继承成员？ | 私人会员？ |
| --- | --- | --- | --- |
| [`getDeclaredConstructor()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredConstructor-java.lang.Class...-) | 没有 | N / A &lt;sup&gt;1&lt;/sup&gt; | 是 |
| [`getConstructor()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getConstructor-java.lang.Class...-) | 没有 | N / A &lt;sup&gt;1&lt;/sup&gt; | 没有 |
| [`getDeclaredConstructors()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredConstructors--) | 是 | N / A &lt;sup&gt;1&lt;/sup&gt; | 是 |
| [`getConstructors()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getConstructors--) | 是 | N / A &lt;sup&gt;1&lt;/sup&gt; | 没有 |

&lt;sup&gt;1&lt;/sup&gt; 构造函数不是继承的。

给定一个类名并指示哪些成员感兴趣， [``ClassSpy``](example/ClassSpy.java)示例使用`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");
    }
}

```

这个例子比较紧凑;然而，由于 [`java.lang.reflect.Member`](https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Member.html) 界面自最早的反射实现以来已经存在，因此`printMembers()`方法略显尴尬，并且当泛型时它无法修改为包含更有用的`getGenericString()`方法介绍了。唯一的选择是如图所示进行测试和铸造，用`printConstructors()`，`printFields()`和`printMethods()`代替该方法，或者对 [`Member.getName()`](https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Member.html#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`](https://docs.oracle.com/javase/8/docs/api/java/lang/RuntimeException.html) 和其他超类中定义的异常链机制构造函数（具有 [`Throwable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html) 参数的构造函数）） 。

```
$ 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`](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/ReadableByteChannel.html) 定义 [`read()`](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/ReadableByteChannel.html#read-java.nio.ByteBuffer-) 。其余方法继承自超级接口。通过将`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()`](https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html#isEnumConstant--) 。 [``EnumSpy`` ](../special/example/EnumSpy.java)示例在该踪迹的后续部分[检查枚举](../special/enumMembers.html)中包含了一种可能的实现方式。

在输出的方法部分中，观察方法名称包含声明类的名称。因此，`toString()`方法由 [`Enum`](https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html#toString--) 实现，不是从 [`Object`](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html) 遗传。可以通过使用 [`Field.getDeclaringClass()`](https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html#getDeclaringClass--) 修改代码以使其更加明显。以下片段说明了潜在解决方案的一部分。

```
if (mbr instanceof Field) {
    Field f = (Field)mbr;
    out.format("  %s%n", f.toGenericString());
    out.format("  -- declared in: %s%n", f.getDeclaringClass());
}

```