Java的反射机制

字数 1040阅读 243

本人翻译并整理自Oracle官方文档

Java的反射机制允许在程序运行时去获取一个类(class)的成员变量和方法。

一、Classes

每个对象不是引用类型就是基本类型。引用类型全部继承自java.lang.Object。类、枚举类型、数组和接口都是引用类型。还有一套固定的基本类型:boolean, byte, short, int, long, char, floatdouble

对于每种类型的对象,Java虚拟机会实例化出一个不可变的java.lang.Class对象的实例,它提供了一些方法去检查这个对象的运行时属性包括它的成员和类型信息。Class类同时也提供了创造新的类和对象的能力。最重要的是它是所有反射API(Reflection APIs)的出发点。

1.1 获得Class对象-反射操作的入口

所有反射操作的出发点是java.lang.Class。因为有java.lang.reflect.ReflectPermission这个异常, java.lang.reflect下的类都没有公共的构造函数。为了获得这些类必须调用Class里的合适方法。这里有若干种方法来获得一个Class对象,取决于代码是否能访问某个对象,是否知道这个类的名称,类型(TYPE),或者一个已有的Class对象。

1.1.1 Object.getClass()

如果一个对象的实例是可以获得的,那么最简单的获得它的Class是调用Object.getClass()。当然它只在这个实例是继承自Object才可行。下面是一些例子:

Class c = "foo".getClass();

byte[] bytes = new byte[1024];
Class c = bytes.getClass();

1.1.2 .class 语法

如果类型是可以获得的但是没有对象的实例,那么可以通过在type后追加".class"来获得它的Class。这也是最简单的方法来获得一个基本类型的Class。

boolean b;
Class c = b.getClass();   // 编译错误

Class c = boolean.class;  // 正确

Class c = int[][][].class;

1.1.3 Class.forName()

如果一个类的 完全限定名(fully-qualified) 可以获得,可以通过静态方法Class.forName()来获得相应的Class。不能被用于基本类型。使用这种方法获得某个数组类的语法是Class.getName()。这个语法适用于引用和原始类型。

Class c = Class.forName("com.duke.MyLocaleServiceProvider");

Class cDoubleArray = Class.forName("[D");  // 与double[].class作用相同

1.1.4 基本类型包装类的TYPE成员变量

除了.class语法外还有一种方式可以获得基本类型的Class。每种基本类型和void都有一个包装类在java.lang被用来基本类型装箱成引用类型。每个包装类包含一个成员变量TYPE相当于Class对基本类型的包装。

Class c = Double.TYPE;

Class c = Void.TYPE;

1.1.5 方法返回的Class

这里有几个Reflection APIs返回Class对象但是需要某个Class已经被直接或间接的获得。

Class.getSuperclass() // 返回某个Class的父类Class

Class.getClasses() // 返回某个类的所有成员的Class对象
Class<?>[] c = Character.class.getClasses(); // Character包含2个成员对象Character.Subset 和Character.UnicodeBlock

1.2 访问Class的声明信息

某个类可能在被声明时使用一个或多个影响运行行为的修饰符。

  • 访问修饰符:public, protectedprivate
  • 要求重写的修饰符:abstract
  • 限制单实例的修饰符:static
  • 禁止修改值的修饰符:final
  • 强制严格浮点行为的修饰符:strictfp
  • 注解

不是所有的修饰符可以用在所有的类上,例如一个接口不能被声明为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]); // 根据输入的类名通过反射获得其Class

            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");
            }

              // 生产环境的代码应该更优雅的处理这个异常
        } 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.util.concurrent.ConcurrentNavigableMap,它的源代码是:

public interface ConcurrentNavigableMap<K,V>
    extends ConcurrentMap<K,V>, NavigableMap<K,V>

注意,既然这是一个接口,那么它隐式的为abstract,编译器会为每个接口添加这个修饰符。

1.3 探索Class的成员

推荐阅读更多精彩内容