Java反射完全解析1

按例,首先描述定义一下。

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
通过反射,Java代码可以发现有关已加载类的字段,方法和构造函数的信息,并可以在安全限制内对这些字段,方法和构造函数进行操作。

简而言之,你可以在运行状态中通过反射机制做到:

  • 对于任意一个类,都能够知道这个类的所有属性和方法;
  • 对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

在我看来我们平时使用Java反射主要涉及两个类(接口)ClassMember,如果把这两个类搞清楚了,反射基本就ok了。

#Class
提到反射就不得不提到Class,Class可以说是反射能够实现的基础;注意这里说的Class与class关键字不是同一种东西。class关键字是在声明java类时使用的;而Class 是java JDK提供的一个类,完整路径为 java.lang.Class,本质上与Math, String 或者你自己定义各种类没什么区别。

1
2
3
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
...
}

那Class到底在反射中起到什么作用呢?

For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all of the Reflection APIs.

对于每一种类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。
比如创建编译一个Shapes类,那么,JVM就会创建一个Shapes对应Class类的Class实例,该Class实例保存了Shapes类相关的类型信息,包括属性,方法,构造方法等等,通过这个Class实例可以在运行时访问Shapes对象的属性和方法等。另外通过Class类还可以创建出一个新的Shapes对象。这就是反射能够实现的原因,可以说Class是反射操作的基础。
需要特别注意的是,每个class(注意class是小写,代表普通类)类,无论创建多少个实例对象,在JVM中都对应同一个Class对象。

下面就通过一个简单的例子来说明如何通过反射实例化一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Animal {
private String name;
private int age;
public Animal(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Animal : name = " + name + " age = " + age;
}
}

public class TestReflection{
private static final String TAG = "Reflection";
public void testReflection(){
//获取Animal类的Class对象
Class c = Animal.class;
try {
//通过Class对象反射获取Animal类的构造方法
Constructor constructor = c.getConstructor(String.class, int.class);
//调用构造方法获取Animal实例
Animal animal = (Animal) constructor.newInstance( "Jack", 3);
//将构造出来的Animal对象打印出来
Log.d(TAG, animal.toString());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

下面我们来看下打印值

1
03-28 20:12:00.958 2835-2835/? D/Reflection: Animal : name = Jack age = 3

可以看出我们确实成功构造出了Animal对象,而且在这过程中Class功不可没。

有人说你这也太费事了,都知道Animal对象了,我分分钟就能给你new出来了。

1
Animal animal = new Animal("Jack", 3);

没错!
但是如果并不能直接导入Animal类呢,如果构造方法都是private的呢?这个时候反射就能大展身手了。

####如何获取Class
说Class是反射能够实现的基础的另一个原因是:Java反射包java.lang.reflect中的所有类都没有public构造方法,要想获得这些类实例,只能通过Class类获取。所以说如果想使用反射,必须得获得Class对象。
下面列举了几种能够获取Class对象的方法。

  • Object.getClass()
    通过对象实例获取对应Class对象,如
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //Returns the Class for String
    Class c = "foo".getClass();

    enum E { A, B }
    //Returns the Class corresponding to the enumeration type E.
    Class c = A.getClass();

    byte[] bytes = new byte[1024];
    //Returns the Class corresponding to an array with component type byte.
    Class c = bytes.getClass();

    Set<String> s = new HashSet<String>();
    //Returns the Class corresponding to java.util.HashSet.
    Class c = s.getClass();

然而对于基本类型无法使用这种方法

1
2
boolean b;
Class c = b.getClass(); // compile-time error

  • The .class Syntax
    通过类的类型获取Class对象,基本类型同样可以使用这种方法,如

    1
    2
    3
    4
    5
    //The `.class` syntax returns the Class corresponding to the type `boolean`.
    Class c = boolean.class;

    //Returns the Class for String
    Class c = String.class;
  • Class.forName()
    通过类的全限定名获取Class对象, 基本类型无法使用此方法

    1
    Class c = Class.forName("java.lang.String");

对于数组比较特殊

1
2
3
Class cDoubleArray = Class.forName("[D");    //相当于double[].class

Class cStringArray = Class.forName("[[Ljava.lang.String;"); //相当于String[][].class

  • TYPE Field for Primitive Type Wrappers
    基本类型和void 类型的包装类可以使用TYPE字段获取

    1
    2
    3
    Class c = Double.TYPE;   //等价于 double.class.

    Class c = Void.TYPE;
  • Methods that Return Classes
    另外还有一些反射方法可以获取Class对象,但前提是你已经获取了一个Class对象。
    有点拗口,比如说你已经获取了一个类的Class对象,就可以通过反射方法获取这个类的父类的Class对象。

Class.getSuperclass()
获得给定类的父类Class

// javax.swing.JButton的父类是javax.swing.AbstractButton
Class c = javax.swing.JButton.class.getSuperclass();

类似方法还有:
Class.getClasses()
Class.getDeclaredClasses()
Class.getDeclaringClass()
Class.getEnclosingClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()