反射
如何偷窥类的隐私(不是)
RTTI(Run-Time Type Identification)运行时类型识别。在《Thinking in Java》一书第十四章中有提到,其作用是在运行时识别一个对象的类型和类的信息。主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
这里我们首先需要理解 Class类,以及类的加载机制; 然后基于此我们如何通过反射获取Class类以及类中的成员变量、方法、构造方法等。
Class类
Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName("类名")等方法获取class对象)。数组同样也被映射为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象。
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
private static final int ANNOTATION= 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
private static native void registerNatives();
static {
registerNatives();
}
/*
* Private constructor. Only the Java Virtual Machine creates Class objects. //私有构造器,只有JVM才能调用创建Class对象
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}到这我们也就可以得出以下几点信息:
Class类也是类的一种,与class关键字是不一样的。
手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件)
每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
Class类只存私有构造函数,因此对应Class对象只能有JVM创建和加载
Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(关于反射稍后分析)。
类加载
类加载机制和类字节码技术可以参考如下两篇文章:
源代码通过编译器编译为字节码,再通过类加载子系统进行加载到JVM中运行
这篇文章将带你深入理解Java 类加载机制
其中,这里我们需要回顾的是:
类加载机制流程

类的加载

反射的使用
Class类及其用法
在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private),下面将对这几个重要类进行分别说明。Class类对象的获取
在类加载的时候,jvm会创建一个class对象
class对象是可以说是反射中最常用的,获取class对象的方式的主要有三种
根据类名:类名.class
根据对象:对象.getClass()
根据全限定类名:Class.forName(全限定类名)
输出结果:
再来看看 Class类的方法
forName()
(1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
(2)为了产生Class引用,forName()立即就进行了初始化。
Object-getClass()
获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName()
取全限定的类名(包括包名),即类的完整名字。
getSimpleName()
获取类名(不包括包名)
getCanonicalName()
获取全限定的类名(包括包名)
isInterface()
判断Class对象是否是表示一个接口
getInterfaces()
返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss()
返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance()
返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
getFields()
获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
getDeclaredFields()
获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。
Constructor类及其用法
获取Constructor对象是通过Class类中的方法获取的,Class类与Constructor相关的主要方法如下:
static Class<?>
forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
Constructor
getConstructor(Class<?>... parameterTypes)
返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[]
getConstructors()
返回所有具有public访问权限的构造函数的Constructor对象数组
Constructor
getDeclaredConstructor(Class<?>... parameterTypes)
返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[]
getDeclaredConstructors()
返回所有声明的(包括private)构造函数对象
T
newInstance()
调用无参构造器创建此 Class 对象所表示的类的一个新实例。
下面看一个简单例子来了解Constructor对象的使用:
输出结果
关于Constructor类本身一些常用方法如下(仅部分,其他可查API)
Class
getDeclaringClass()
返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类,其实就是返回真实类型(不包含参数)
Type[]
getGenericParameterTypes()
按照声明顺序返回一组 Type 对象,返回的就是 Constructor对象构造函数的形参类型。
String
getName()
以字符串形式返回此构造方法的名称。
Class<?>[]
getParameterTypes()
按照声明顺序返回一组 Class 对象,即返回Constructor 对象所表示构造方法的形参类型
T
newInstance(Object... initargs)
使用此 Constructor对象表示的构造函数来创建新实例
String
toGenericString()
返回描述此 Constructor 的字符串,其中包括类型参数。
Field类及其用法
同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下:
Field
getDeclaredField(String name)
获取指定name名称的(包含private修饰的)字段,不包括继承的字段
Field[]
getDeclaredFields()
获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
Field
getField(String name)
获取指定name名称、具有public修饰的字段,包含继承字段
Field[]
getFields()
获取修饰符为public的字段,包含继承字段
下面的代码演示了上述方法的使用过程
上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用Class类的getField/getFields,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。下面将通过Field类本身的方法对指定类属性赋值,代码演示如下:
其中的set(Object obj, Object value)方法是Field类本身的方法,用于设置字段的值,而get(Object obj)则是获取字段的值,当然关于Field类还有其他常用的方法如下:
void
set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
Object
get(Object obj)
返回指定对象上此 Field 表示的字段的值
Class<?>
getType()
返回一个 Class 对象,它标识了此Field 对象所表示字段的声明类型。
boolean
isEnumConstant()
如果此字段表示枚举类型的元素则返回 true;否则返回 false
String
toGenericString()
返回一个描述此 Field(包括其一般类型)的字符串
String
getName()
返回此 Field 对象表示的字段的名称
Class<?>
getDeclaringClass()
返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段
void
setAccessible(boolean flag)
将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性
上述方法可能是较为常用的,事实上在设置值的方法上,Field类还提供了专门针对基本数据类型的方法,如setInt()/getInt()、setBoolean()/getBoolean、setChar()/getChar()等等方法,这里就不全部列出了,需要时查API文档即可。需要特别注意的是被final关键字修饰的Field字段是安全的,在运行时可以接收任何修改,但最终其实际值是不会发生改变的。
Method类及其用法
下面是Class类获取Method对象相关的方法:
Method
getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[]
getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method
getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[]
getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
同样通过案例演示上述方法:
输出结果:
在通过getMethods方法获取Method对象时,会把父类的方法也获取到,如上的输出结果,把Object类的方法都打印出来了。而getDeclaredMethod/getDeclaredMethods方法都只能获取当前类的方法。我们在使用时根据情况选择即可。下面将演示通过Method对象调用指定类的方法:
输出结果
在上述代码中调用方法,使用了Method类的invoke(Object obj,Object... args)第一个参数代表调用的对象,第二个参数传递的调用方法的参数。这样就完成了类方法的动态调用。
Object
invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
Class<?>
getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型,即方法的返回类型
Type
getGenericReturnType()
返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象,也是方法的返回类型。
Class<?>[]
getParameterTypes()
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。即返回方法的参数类型组成的数组
Type[]
getGenericParameterTypes()
按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的,也是返回方法的参数类型
String
getName()
以 String 形式返回此 Method 对象表示的方法名称,即返回方法的名称
boolean
isVarArgs()
判断方法是否带可变参数,如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false。
String
toGenericString()
返回描述此 Method 的字符串,包括类型参数。
getReturnType方法/getGenericReturnType方法都是获取Method对象表示的方法的返回类型,只不过前者返回的Class类型后者返回的Type(前面已分析过),Type就是一个接口而已,在Java8中新增一个默认的方法实现,返回的就参数类型信息
而getParameterTypes/getGenericParameterTypes也是同样的道理,都是获取Method对象所表示的方法的参数类型,其他方法与前面的Field和Constructor是类似的。
更多API
更多API详见java文档。
最后更新于
这有帮助吗?