一、概述
1. 简介
Java反射(Reflection
)机制就是在运行状态中,对于任意一个类,都能够知道这个类的属性和方法。对于任意一个对象能够调用它的任意一个属性和方法。这种动态获取的信息和动态调用对象的方法的功能称为Java语言的反射机制。Java程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。反射机制就是通过java.lang.Class
类来实现的,在Java中,Object 类是所有类的根类,而Class类就是描述Java类的类。
注:因为Class类也是类,所以Object也包括Class类。
2. 主要功能
Java反射框架主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法;
- 修改构造函数、方法、属性的可见性。
3. 主要用途
反射最重要的用途就是开发各种通用框架。很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。
二、反射的使用
1. 获取Class对象
反射的各种功能都需要通过Class对象来实现,因此,需要知道如何获取Class对象,主要有以下几种方式。
使用 Class.forName() 的静态方法
Class.forName(String className)
方法可以通过类或接口的名称(一个字符串或完全限定名)来获取对应的Class对象。
1 | Class<?> cls = Class.forName("com.blinkfox.Zealot"); |
直接获取某个类的class(最安全/性能最好)
1 | Class<String> cls = String.class; |
调用某个对象的 getClass() 方法
1 | Class<String> cls = str.getClass(); |
2. 判断是否为某个类的实例
一般地,我们用instanceof
关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()
方法来判断是否为某个类的实例,它是一个Native方法:
1 | public native boolean isInstance(Object obj); |
3. 创建实例
通过反射来生成对象主要有两种方式。
使用Class对象的newInstance()方法
1 | Class<?> c = String.class; |
通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法
1 | // 获取String所对应的Class对象 |
注:这种方法可以用指定的构造器构造类的实例。
4. 获取方法
获取某个Class对象的方法集合,主要有以下几个方法:
getDeclaredMethods()
方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
1 | public Method[] getDeclaredMethods() throws SecurityException |
getMethods()
方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
1 | public Method[] getMethods() throws SecurityException |
getMethod()
方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。
1 | public Method getMethod(String name, Class<?>... parameterTypes) |
代码示例:
1 | import java.lang.reflect.InvocationTargetException; |
注:通过
getMethods()
获取的方法可以获取到父类的方法,比如java.lang.Object
下定义的各个方法。
5. 获取构造方法
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor
方法得到Constructor
类的一个实例,而Constructor类有一个newInstance
方法可以创建一个对象实例:
1 | public T newInstance(Object ... initargs) |
注:此方法可以根据传入的参数来调用对应的Constructor创建对象实例。
6. 获取类的成员变量信息
获取的方法同Method相似,主要是这几个方法,在此不再赘述:
Field getField(String name)
: 访问公有的成员变量。Field[] getDeclaredFields()
:所有已声明的成员变量。但不能得到其父类的成员变量。Field[] getFields()
和Field[] getDeclaredFields()
用法同上。
7. 调用方法
当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:
1 | public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, |
代码示例:
1 | public class Test { |
8. 利用反射创建数组
数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference
。下面我们看一看利用反射创建数组的例子:
1 | public static void testArray() throws ClassNotFoundException { |
三、使用反射获取信息
Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,因此我们只是做简单的介绍,详细请参考JDK文档。
1. 获取类内信息
- 构造器:
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()
2. 判断类本身信息的方法
- 是否注解类型:
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)
3. 使用反射获取泛型信息
为了通过反射操作泛型以迎合实际开发的需要, Java新增了java.lang.reflect.ParameterizedType
、java.lang.reflect.GenericArrayType
、java.lang.reflect.TypeVariable
、java.lang.reflect.WildcardType
几种类型来代表不能归一到Class类型但是又和原始类型同样重要的类型。
ParameterizedType
: 一种参数化类型, 比如CollectionGenericArrayType
: 一种元素类型是参数化类型或者类型变量的数组类型TypeVariable
: 各种类型变量的公共接口WildcardType
: 一种通配符类型表达式, 如?
、? extends Number
、? super Integer
代码示例:
1 | public class Client { |
参考文档:Java反射基础