1、什么是反射
在了解反射之前,我们都是如何使用一个Java类的?
- 已知一个类的类名,以及类中的方法、属性和构造方法等
- 调用构造方法创建对象
- 使用对象调用方法或者属性
那么问题来了,如果我们只知道一个类的类名,能否动态得到类的信息,包括其方法和属性?
- 通过反射
所以反射的概念是:在运行状态中,对任意类都可知道其所有属性和方法,对任意对象都可调用其方法和属性。这种动态获取信息以及动态调用对象方法的功能,称之为Java的反射机制。
如Spring框架只需要通过xml文件描述类的基本信息,就可以利用反射机制动态装配对象。
2、反射的相关类
和反射相关的类主要包括 Class、Constructor、Method、Field等,除了Class,其他的类都位于java.lang.reflect包中。其中最重要的类是Class,可以说,反射的使用都是从Class开始的。
2.1 Class类
2.1.1 Class实例的获取
把一个类封装成为Class类,Class实例主要有三种方式获取:
- //使用对象名获取
- String str = "hello";
- Class clazz = str.getClass();
- //使用类名获取,在类名加后缀 .class
- Class clazz = String.class;
- //使用类名获取,通过Class的静态方法forName()
- Class clazz = Class.forName("java.lang.String");
2.1.2 Class类的主要方法
Class类中的主要方法有:
- getMethod:返回类中某个方法的实例
- getMethods:返回类中所有方法的实例
- getField:返回类中某个属性的实例
- getFields:返回类中所有属性的实例
- getConstructor:返回类中的一个构造方法的实例
- getXXX:Class中还有若干get方法,获取类的基本信息
需要注意的是,以上涉及的获取Method或Field实例的方法,实际上还有一个类似的方法,不过名字中多个词Declared,如getDeclaredMethods(),区别在于:
- 包含Declared:它仅获取自身类声明的方法,包括公开的、私有的、保护的等
- 没有Delcared的普通get方法:会获取某个类所有的公开(public)成员,包括自己定义的和继承下来的
简单示例:
public static void main(String[] args) {
Class clazz_str = String.class;
Method[] methods = clazz_str.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
}
7
1
public static void main(String[] args) {
2
Class clazz_str = String.class;
3
Method[] methods = clazz_str.getMethods();
4
for (Method method : methods) {
5
System.out.println(method.getName());
6
}
7
}
2.2 Constructor类
2.2.1 实例的获取
Constructor类的对象是由Class对象获取,Class类中定义了如下方法:
- Constructor<T> getConstructor(Class... parameterTypes):通过指定参数类型,返回构造方法实例
- Constructor[] getConstructors():返回该类的所有构造方法实例
这里所说的指定的参数类型,是指实际构造方法的形参。比如有构造方法 public Student(String name),那么需要:
Class stuClazz = Student.class;
Constructor stuCon = stuClazz.getConstructor(String.class);
2
1
Class stuClazz = Student.class;
2
Constructor stuCon = stuClazz.getConstructor(String.class);
2.2.2 实例的基本信息获取
该类可以通过getXXX的方法,获取构造方法的基本信息,如:
- getName:返回构造方法的名字
- getParameterTypes:返回构造方法的参数类型
2.2.3 创建对象
除了构造方法的基本信息,也可以通过该Constructor的实例创建对象:
- newInstance(Object... initargs);
示例:
Class stuClazz = Student.class;
Constructor stuCon = stuClazz.getConstructor(String.class);
Student stu = (Student)stuCon.newInstance("zhangsan");
3
1
Class stuClazz = Student.class;
2
Constructor stuCon = stuClazz.getConstructor(String.class);
3
Student stu = (Student)stuCon.newInstance("zhangsan");
2.3 Method类
2.3.1 Method的获取
Method类的实例由Class对象获取:
- Method getMethod(String name, Class... parameterTypes):通过指定方法名,参数类型,返回Method实例
- Method[] getMethods():返回该类中所有方法的Method实例
2.3.2 Method获取基本信息
Method封装了类中的方法,可以动态获取方法的信息,如:
- getReturnType:获取方法的返回值类型
- getName:获得方法名
- getParameterTypes:获得方法参数类型
2.3.3 Method动态调用方法
除了动态获得方法信息,Method类还可以动态调用某个对象的具体方法:
- invoke(Object obj, Object... args);
示例:
public static void main(String[] args) throws Exception {
Class stuClazz = Student.class;
Constructor stuCon = stuClazz.getConstructor(String.class);
Student student = (Student)stuCon.newInstance("zhangsan");
Method study = stuClazz.getMethod("study");
study.invoke(student);
Method studyWithOne = stuClazz.getMethod("studyWithOne", String.class);
studyWithOne.invoke(student, "lisi");
}
x
1
public static void main(String[] args) throws Exception {
2
Class stuClazz = Student.class;
3
Constructor stuCon = stuClazz.getConstructor(String.class);
4
Student student = (Student)stuCon.newInstance("zhangsan");
5
6
Method study = stuClazz.getMethod("study");
7
study.invoke(student);
8
9
Method studyWithOne = stuClazz.getMethod("studyWithOne", String.class);
10
studyWithOne.invoke(student, "lisi");
11
}
2.4 Field类
2.4.1 Field的获取
获得Field实例,都是通过Class中的方法实现的:
- public Field getField(String name)
- 通过指定Field的名字,返回Field实例
- 注意Field的访问权限
之前提到过,如果单纯地使用getField那么获取到的方法实例都是public的,如果要获取private方法,需要使用诸如getDeclaredField方法:
//如有Student类,有private权限的属性name
Class stuClazz = Student.class;
Field name = stuClazz.getField("name"); // NG,报异常,找不到该方法
Field name = stuClazz.getDeclaredField("name"); // OK
4
1
//如有Student类,有private权限的属性name
2
Class stuClazz = Student.class;
3
Field name = stuClazz.getField("name"); // NG,报异常,找不到该方法
4
Field name = stuClazz.getDeclaredField("name"); // OK
备注:
Java Field.get()取得对象的Field属性值,即field.get(Class clazz)可以获取对象属性的值,类似于调用无限制的getter方法
3、反射的重要说明
反射实际上将所有的类成分映射成为对应的的Class对象,这里的Class对象其实可以堪称是一种类型。并不知只有类才能转换成Class对象。
也即是说,如果真的需要(比如某些方法中要求参数是Class类型),像基本的数据类型(boolean、byte、char、short、int、long、float、double)甚至关键字如 void 也可以表示为Class对象(形如 int.class)
比如某些方法,如果你要用反射表示,可能参数可能会写成如 (String.class, int.class) 这种形式,这是sun公司定义的规则,不需要深刻去理解为什么。
例如 getConstructor(Class<?>... parameterTypes):
Class stuClazz = Student.class;
Constructor stuCon = stuClazz.getConstructor(String.class);
1
Class stuClazz = Student.class;
2
Constructor stuCon = stuClazz.getConstructor(String.class);