译自:http://docs.oracle.com/javase/tutorial/reflect/class/classModifiers.html
一个类可以在声明时使用一些修饰符,这些修饰符会影响其运行时行为:
访问修饰符:public,protected和private
要求重写(override):abstract
限定为一个实例:static
禁止修改:final
强制精确浮点:strictfp
- 注解(Annotations)
并不是所有的类都可以用任意修饰符进行修饰,比如接口不能使用final修饰而枚举类型不能用abstract修饰。java.lang.reflect.Modifier包含类所有类型的修饰符。同时它也包含了可以解析由Class.getModifiers()返回的修饰符的方法。
下面这段代码例子展示了如何获取一个类的包括修饰符、泛型类型参数、实现的接口和继承路径在内的声明组件。由于Class类实现了java.lang.reflect.AnnotatedElement接口,所以同时也可以获取到运行时注解。
import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Arrays; import java.util.ArrayList; import java.util.List; import static java.lang.System.out; public class ClassDeclarationSpy { public static void main(String... args) { try { Class<?> c = Class.forName(args[0]); out.format("Class:%n %s%n%n", c.getCanonicalName()); out.format("Modifiers:%n %s%n%n", Modifier.toString(c.getModifiers())); out.format("Type Parameters:%n"); TypeVariable[] tv = c.getTypeParameters(); if (tv.length != 0) { out.format(" "); for (TypeVariable t : tv) out.format("%s ", t.getName()); out.format("%n%n"); } else { out.format(" -- No Type Parameters --%n%n"); } out.format("Implemented Interfaces:%n"); Type[] intfs = c.getGenericInterfaces(); if (intfs.length != 0) { for (Type intf : intfs) out.format(" %s%n", intf.toString()); out.format("%n"); } else { out.format(" -- No Implemented Interfaces --%n%n"); } out.format("Inheritance Path:%n"); List<Class> l = new ArrayList<Class>(); printAncestor(c, l); if (l.size() != 0) { for (Class<?> cl : l) out.format(" %s%n", cl.getCanonicalName()); out.format("%n"); } else { out.format(" -- No Super Classes --%n%n"); } out.format("Annotations:%n"); Annotation[] ann = c.getAnnotations(); if (ann.length != 0) { for (Annotation a : ann) out.format(" %s%n", a.toString()); out.format("%n"); } else { out.format(" -- No Annotations --%n%n"); } // production code should handle this exception more gracefully } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static void printAncestor(Class<?> c, List<Class> l) { Class<?> ancestor = c.getSuperclass(); if (ancestor != null) { l.add(ancestor); printAncestor(ancestor, l); } } }
下面是一些实例输出,用户输入用斜体标出。
$ 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 --
这是实际中java.utiol.concurrent.ConcurrentNavigableMap类在源代码中的声明:
public interface ConcurrentNavigableMap<K,V> extends ConcurrentMap<K,V>, NavigableMap<K,V>
注意由于这是一个接口,所以被声明为abstract。编译器会为每一个接口添加abstract修饰符。同样,这个声明包括两个泛型类型参数,K和V。示例代码仅仅打印了这些参数的名字,但实际上是可以通过使用java.lang.reflect.TypeVariable中的方法获取它们的附加信息的。接口同样可以实现其他接口,就像上面那样。
$ java ClassDeclarationSpy "[Ljava.lang.String;" Class: java.lang.String[] Modifiers: public abstract final Type Parameters: -- No Type Parameters -- Implemented Interfaces: interface java.lang.Cloneable interface java.io.Serializable Inheritance Path: java.lang.Object Annotations: -- No Annotations --
由于数组是运行时对象,所有的类型信息都是由JVM定义的。尤其是数组实现了Cloneable和java.io.Serializable接口而它们的直接父类都是Object。
$ java ClassDeclarationSpy java.io.InterruptedIOException Class: java.io.InterruptedIOException Modifiers: public Type Parameters: -- No Type Parameters -- Implemented Interfaces: -- No Implemented Interfaces -- Inheritance Path: java.io.IOException java.lang.Exception java.lang.Throwable java.lang.Object Annotations: -- No Annotations --
从继承路径中可以看出java.io.InterruptedIOException是一个受检异常因为RuntimeException并没有出现。
$ java ClassDeclarationSpy java.security.Identity Class: java.security.Identity Modifiers: public abstract Type Parameters: -- No Type Parameters -- Implemented Interfaces: interface java.security.Principal interface java.io.Serializable Inheritance Path: java.lang.Object Annotations: @java.lang.Deprecated()
这些输出显示java.security.Identity这个被弃用的API使用类注解java.lang.Deprecated。这可以被反射代码用于探测被弃用的API。
注意:并不是所有的注解都可以通过反射获取。只有那些java.lang.annotation.RetentionPolicy字段值为RUNTIME的可以被获取。在java预定义的三个注解@Deprecated,@Override和@SuppressWarnings中只有@Deprecated是符合的。