可以分析类能力的程序称为反射(reflective)。反射机制的功能很是强大,主要提供了以下功能:html
对于任意一个类,都可以知道这个类的全部属性和方法;java
对于任意一个对象,都可以调用它的任意方法和属性;api
在程序运行期间,Java运行时系统始终为全部的对象维护一个被称为运行时的类型标识。这个信息跟踪着每一个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。然而,能够经过专门的Java类访问这些信息,这个类为java.lang.Class。数组
类Class的实例表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注释是一种接口。每一个数组也属于一个类,它被反映为具备相同元素类型和维数的全部数组共享的Class对象。 原始Java类型(boolean,byte,char,short,int,long,float和double)和关键字void也表示为Class对象。安全
Class类没有公共构造函数。Class对象由Java虚拟机自动构建,由于加载了类,而且经过调用类加载器中的defineClass方法。oracle
虽然咱们不能new一个Class对象,可是却能够经过已有的类获得一个Class对象,共有以下三种方式:ide
一、Class类的forName方法函数
Class<?> clazz = Class.forName("com.codersm.study.jdk.reflect.Person");
复制代码
二、经过一个类的对象的getClass()方法测试
Class<?> clazz = new Person().getClass();
复制代码
三、Class字面常量this
Class<Person> clazz = Person.Class;
复制代码
小结:
注意调用forName方法时须要捕获一个名称为ClassNotFoundException的异常,由于forName方法在编译器是没法检测到其传递的字符串对应的类是否存在的,只能在程序运行时进行检查,若是不存在就会抛出ClassNotFoundException异常。
Class字面常量这种方法更加简单,更安全。由于它在编译器就会受到编译器的检查同时因为无需调用forName方法效率也会更高,由于经过字面量的方法获取Class对象的引用不会自动初始化该类?。 更加有趣的是字面常量的获取Class对象引用方式不只能够应用于普通的类,也能够应用用接口,数组以及基本数据类型。
Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大体包含以下方法,其中每一个方法都包含多个重载版本,所以咱们只是作简单的介绍,详细请参考JDK文档。
获取类信息
获取内容 | 方法签名 |
---|---|
构造器 | Constructor<T> getConstructor(Class<?>... parameterTypes) |
方法 | Method getMethod(String name, Class<?>... parameterTypes) |
属性 | Field getField(String name) |
Annotation | <A extends Annotation> A getAnnotation(Class<A> annotationClass) |
内部类 | Class<?>[] getDeclaredClasses() |
外部类 | Class<?> getDeclaringClass() |
实现的接口 | Class<?>[] getInterfaces() |
修饰符 | int getModifiers() |
所在包 | Package getPackage() |
类名 | String getName() |
简称 | String getSimpleName() |
注: getDeclaredXxx方法能够获取全部的Xxx,不管private/public。
判断类自己信息的方法
判断内容 | 方法签名 |
---|---|
注解类型? | boolean isAnnotation() |
使用了该Annotation修饰? | boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) |
匿名类? | boolean isAnonymousClass() |
数组? | boolean isArray() |
枚举? | boolean isEnum() |
原始类型? | boolean isPrimitive() |
接口? | boolean isInterface() |
obj是不是该Class的实例 | boolean isInstance(Object obj) |
使用反射生成并操做对
程序能够经过Method对象来执行相应的方法;
经过Constructor对象来调用对应的构造器建立实例;
经过Filed对象直接访问和修改对象的成员变量值。
经过反射来生成对象的方式有两种:
使用Class对象的newInstance()方法来建立该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器)。
先使用Class对象获取指定的Constructor对象, 再调用Constructor对象的newInstance()方法来建立该Class对象对应类的实例(经过这种方式能够选择指定的构造器来建立实例)。
class Person {
private String name;
private Integer age;
public Person() {
this.name = "system";
this.age = 99;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Integer getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) throws Exception {
Class<Person> pClass = Person.class;
// 经过第1种方式建立对象
Person p = pClass.newInstance();
System.out.println(p);
// 经过第2种方式建立对象
Constructor<Person> constructor = pClass.getDeclaredConstructor(
String.class, Integer.class);
Person person2 = constructor.newInstance("zhangsan",20);
System.out.println(person2);
}
}
复制代码
当获取到某个类对应的Class对象以后, 就能够经过该Class对象的getMethod来获取一个Method数组或Method对象.每一个Method对象对应一个方法,在得到Method对象以后,就能够经过调用invoke方法来调用该Method对象对应的方法。
Person person = new Person();
// 获取getAge方法
Method getAgeMethod = person.getClass().getMethod("getAge",null);
// 调用invoke方法来调用getAge方法
Integer age = (Integer) getAgeMethod.invoke(person,null);
System.out.println(age);
复制代码
经过Class对象的的getField()方法能够获取该类所包含的所有或指定的成员变量Field,Filed提供了以下两组方法来读取和设置成员变量值.
getXxx(Object obj): 获取obj对象的该成员变量的值, 此处的Xxx对应8中基本类型,若是该成员变量的类型是引用类型, 则取消get后面的Xxx;
setXxx(Object obj, Xxx val): 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型, 若是该成员类型是引用类型, 则取消set后面的Xxx;
Person person = new Person();
// 获取name成员变量Field
Field nameField = person.getClass().getDeclaredField("name");
// 启用访问控制权限
nameField.setAccessible(true);
// 获取person对象的成员变量name的值
String name = (String) nameField.get(person);
System.out.println("name = " + name);
// 设置person对象的成员变量name的值
nameField.set(person, "lisi");
System.out.println(person);
复制代码
在Java的java.lang.reflect包中存在着一个能够动态操做数组的类,Array提供了动态建立和访问Java数组的方法。Array容许在执行get或set操做进行取值和赋值。在Class类中与数组关联的方法是:
方法 | 说明 |
---|---|
public native Class<?> getComponentType() | 返回表示数组元素类型的Class,即数组的类型 |
public native boolean isArray() | 断定此Class对象是否表示一个数组类 |
java.lang.reflect.Array中的经常使用静态方法以下:
newInstance(Class<?> componentType, int length)
newInstance(Class<?> componentType, int... dimensions)
int getLength(Object array)
Object get(Object array, int index)
void set(Object array, int index, Object value)
实现通用数组复制功能,其代码以下:
public class GenericityArray {
public static <T> T[] copy(T[] clazz) {
return (T[]) Array.newInstance(
clazz.getClass().getComponentType(),
clazz.length);
}
public static void main(String[] args) {
Integer[] array = {1, 2, 3};
Integer[] copyArray = GenericityArray.copy(array);
System.out.println(copyArray.length);
}
}
复制代码
为了经过反射操做泛型以迎合实际开发的须要, Java新增了java.lang.reflect.ParameterizedType
、 java.lang.reflect.GenericArrayType
、java.lang.reflect.TypeVariable
、java.lang.reflect.WildcardType
。
类型 | 含义 |
---|---|
ParameterizedType | 一种参数化类型, 好比Collection |
GenericArrayType | 一种元素类型是参数化类型或者类型变量的数组类型 |
TypeVariable | 各类类型变量的公共接口 |
WildcardType | 一种通配符类型表达式, 如? extends Number |
其中, 咱们可使用ParameterizedType来获取泛型信息.
public class Client {
private Map<String, Object> objectMap;
public void test(Map<String, User> map, String string) {
}
public Map<User, Bean> test() {
return null;
}
/** * 测试属性类型 * * @throws NoSuchFieldException */
@Test
public void testFieldType() throws NoSuchFieldException {
Field field = Client.class.getDeclaredField("objectMap");
Type gType = field.getGenericType();
// 打印type与generic type的区别
System.out.println(field.getType());
System.out.println(gType);
System.out.println("**************");
if (gType instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) gType;
Type[] types = pType.getActualTypeArguments();
for (Type type : types) {
System.out.println(type.toString());
}
}
}
/** * 测试参数类型 * * @throws NoSuchMethodException */
@Test
public void testParamType() throws NoSuchMethodException {
Method testMethod = Client.class.getMethod("test", Map.class, String.class);
Type[] parameterTypes = testMethod.getGenericParameterTypes();
for (Type type : parameterTypes) {
System.out.println("type -> " + type);
if (type instanceof ParameterizedType) {
Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
for (Type actualType : actualTypes) {
System.out.println("\tactual type -> " + actualType);
}
}
}
}
/** * 测试返回值类型 * * @throws NoSuchMethodException */
@Test
public void testReturnType() throws NoSuchMethodException {
Method testMethod = Client.class.getMethod("test");
Type returnType = testMethod.getGenericReturnType();
System.out.println("return type -> " + returnType);
if (returnType instanceof ParameterizedType) {
Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments();
for (Type actualType : actualTypes) {
System.out.println("\tactual type -> " + actualType);
}
}
}
}
复制代码
使用反射获取注解信息的相关介绍, 请参看Java注解实践