Java的反射

Java反射相关内容。

Class对象

Java中的反射最关键的一个类就是Class类,首先你要先得到这个类的对象,一般有两种方式,如果在编译时你知道那个类名,你可以直接Class cls = MyObject.class;来得到Class对象。如果编译时不知道类名,那需要使用Class = Class.forname("com.mark.MyObject");来得到Class对象,这里需要类的全限定名。

当得到Class对象后,那基本就能通过Class对象得到关于这个类的一切数据:字段、方法、构造函数等等信息都可以通过这个Class对象的相对应方法来获取到。

内部类

示例代码:

1
2
3
4
5
6
7
8
9
package com.mark.reflection;
import java.util.List;
public class GenericClass {
class InnerClass {
public void method() {
System.out.println("InnerClass method");
}
}
}

我们知道内部类的类名是外部类+$+内部类名,所以,我门可以这样加载内部类:

1
Class cls = Class.forName("com.mark.reflection.GenericClass$InnerClass");

这样我们得到了内部类的Class对象,但是如果想试用Class对象来创建内部类实例,则会遇到问题。

1
GenericClass.InnerClass ic = (GenericClass.InnerClass) cls.getConstructor().newInstance(gc);

上面的代码运行将会报NoSuchMethodException的错误,也就是说没有找到无参构造函数。因为这个内部类并不是静态的,它需要一个外部类的实例引用。通过断点,可以发现在Class的getConstructor方法调用了getConstructor0())方法,在getConstructor0())方法中通过得到类的所有构造函数,然后比较参数来得到指定的构造函数,而通过debug发现,这个类只有一个带有GenericClass参数的构造函数。

所以可以使用下面的方式来实例化一个内部类。

1
2
3
Class outerCls = cls.getEnclosingClass();
GenericClass<String> gc = (GenericClass<String>) outerCls.<String>newInstance();
GenericClass.InnerClass ic = (GenericClass.InnerClass) cls.getConstructor(GenericClass.class).newInstance(gc);

getEnclosingClass方法可以获得外部类的Class对象。

上面的方式实例化内部类略显麻烦,我们可以直接加载外部类的Class,然后实例化外部类对象再实例化内部类。

1
2
3
Class cls = Class.forName("com.mark.reflection.GenericClass");
GenericClass<String> gc = (GenericClass<String>) cls.<String>newInstance();
GenericClass.InnerClass ic = gcs.new InnerClass();

数组类型

1
2
3
Class cls = int[].class;
Class cls = Class.forName("[I");
System.out.println(cls.getComponentType());

可以通过上面两种方式来得到int数组的Class对象,其他的基本类型数组也是类似的。如果不知道forName的参数是什么,可以System.out.println(boolean[].class);看看就知道了。通过cls.getComponentType()可以得到数组元素的类型。

数组实例无法直接cls.newInstance()得到,需要借助java.lang.reflect.Array类。

1
int[] ints = (int[]) Array.newInstance(int.class, 2);

创建一个长度为2的int数组。这也适用其他类型数组。

泛型信息

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
package com.mark.reflection;
import java.util.List;
public class GenericClass<T> {
public T val;
public List<T> list;
public T method1() {
return null;
}
public List<String> method2() {
return null;
}
}

对于泛型类,可以通过Class对象得到泛型相关的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Class cls = Class.forName("com.mark.reflection.GenericClass");
Method method = cls.getMethod("method1", null);
System.out.println(method.getGenericReturnType()); // T
System.out.println(method.getReturnType()); // class java.lang.Object
method = cls.getMethod("method2", null);
System.out.println(method.getGenericReturnType()); // java.util.List<T>
System.out.println(method.getReturnType()); // interface java.util.List
Field field = cls.getField("val");
System.out.println(field.getGenericType()); // T
System.out.println(field.getType()); // class java.lang.Object

method = cls.getMethod("method4", List.class);
Parameter parameter = method.getParameters()[0];
Type type = parameter.getType();
System.out.println(type); // interface java.util.List
type = parameter.getParameterizedType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
System.out.println(pt); // java.util.List<java.lang.String>
System.out.println(Arrays.toString(pt.getActualTypeArguments())); //[class java.lang.String]
}