学习目的
- 了解反射的概念与作用意义
- 掌握java提供的反射机制使用到的包和类
- 了解反射机制的实现原理与Spring框架的结合
一.反射
- 概念
在日常开发中,通常的开发步骤是:程序员建立项目 --> 编写源代码 --> 编译得到.class字节码文件 --> java虚拟机JVM加载字节码文件 --> 执行运行。
而反射的步骤则是:通过编译好的字节码文件 --> 创建字节码对象 --> 对编译后的文件进行操作,操作包括创建实例对象、读取或修改对象属性、获取和执行对象方法等。
- 实质
反射对字节码文件内容的操作都基于"字节码对象",而对实例对象、属性、方法等也有专属的反射"类"对象。
- 原则
一切皆对象。
1.1 java.lang.reflect反射包
- 定义
提供用于获取类和对象的反射信息(字节码信息) 的类和接口。
- 实质
提供操作字节码文件的"操作对象"。
- 反射常用接口
- 反射常用类
- Class:所有类或接口的"类型";
- Constructor:构造器类;
- Field:属性类;
- Method:方法类;
- Modifier:修饰符类;
- 反射最重要方法
-
Object invoke(Object obj, Object... args):该方法属于Method类中的方法,obj为字节码对象创建出的一个实例对象;使用Method对象调用该方法意为 调用obj对象的该Method方法,并且方法的参数为...args。
// 使用反射机制来调用一个对象的方法
// 创建字节码对象
Class userClass = Class.forName("com.java.service.User");
// 通过字节码反射创建实例对象
Object obj = userServiceClass.newInstance();
// 获取字节码对象中的Method对象(实则为一个方法),通过"方法名"和参数的类型(重载根据参数列表区分)
Method loginMethod = userClass.getDeclaredMethod("login", String.class, String.class);
//Method loginMethod = userClass.getDeclaredMethod("login", int.class);
// 调用方法4要素:Method方法对象,实例对象Obj,参数,返回值
// 通过Method对象.invoke()执行obj对象的Method方法,参数为...
Object retValue = loginMethod.invoke(obj, "admin","123123");
System.out.println(retValue);
1.2 java.lang.reflect.Type接口
- 定义
Type 是Java中所有"类型"的公共高级接口,包括原始类型、参数化类型、数组类型、类型变量和基本类型等。
- 实质
"数据类型",包括类的类型、参数的类型、元素的类型、返回值的类型等等。
1.3 java.lang.Class<T>类
- 定义
Class类表示Java应用程序中的类和接口,包括我们自定义的"class类" 和 "interface接口"等。Class类用于反射指的是字节码文件内容中代表的"类型",如public类的字节码文件名代表的就是该"类",是一个Class。Class还实现了Type接口。
- 实质
Class类代表所有的"类" 和 "接口"。
- 特点
Class没有公共构造方法(只有一个private的构造 类加载器的构造器),Class对象是在加载类时由Java虚拟机以及 通过调用类加载器中的defineClass方法自动构造的。
- 常用方法
- String toString():将该Class对象转换为字符串,返回的字符串表示形式为 "class" / "interface" + 空格 + 该Class对象代表的完整类名;
-
Class<?> forName(String className):返回 给定字符串名的类或接口相关的 Class字节码对象,采用默认的类加载器;
- Class<?> forName(String name, boolean initialize,ClassLoader loader):返回 给定字符串名的类或接口相关的 Class字节码对象,采用指定的类加载器,并有可能初始化该字节码对象;
-
T newInstance():创建此Class字节码对象所代表的类的一个新实例;
- boolean isInstance(Object obj):判断obj对象是否 是该Class字节码对象代表的类的一个实例;
- boolean isInterface():判定该Class字节码对象是否代表一个接口类型;
- boolean isArray():判定该Class字节码对象是否代表一个数组类型;
- boolean isPrimitive():判定该Class字节码对象是否代表一个基本类型;
- boolean isAnnotation():判定该Class字节码对象是否代表一个注释类型;
-
String getName():返回该Class字节码对象所代表类的全(路径)类名;
- String getSimpleName():返回该Class字节码对象所代表类的简类名;
- ClassLoader getClassLoader():返回加载该Class字节码对象的类加载器;
- Class<? super T> getSuperclass():返回该Class字节码对象所代表的类的父类或父接口;
- Class<?>[] getInterfaces():返回该Class字节码对象所代表的类所实现的所有接口,使用数组存储;
-
int getModifiers():返回该Class字节码对象所代表的类的修饰符,返回的是表示该类修饰符的int数(需要配合Modifier类的toString()方法将其转换为字符串);
- Class<?> getDeclaringClass():若该Class字节码对象是另一个类或接口的成员,返回该Class字节码对象的声明类或接口;
- boolean isAnonymousClass():判断该Class字节码对象底层是否是内部类;
- boolean isLocalClass():判断该Class字节码对象底层是否是native本地类;
-
Field getField(String name):返回一个Field对象,代表该Class字节码对象所代表的类或接口的"公共"成员属性;
-
Method getMethod(String name, Class<?>... parameterTypes):返回一个Method对象,根据指定的方法名和参数类型查找返回;
-
Constructor<T> getConstructor(Class<?>... parameterTypes):返回一个Constructor对象,根据参数类型查找返回(仅返回public构造器);
-
Field[] getDeclaredFields():返回一个Field数组对象,代表该Class字节码对象所代表的类或接口的所有成员属性(包括公开的私有的);
-
Method[] getDeclaredMethods():返回一组 包含了该字节码对象所有的Method方法对象;
- Constructor<?>[] getDeclaredConstructors():返回一组 Constructor对象,包含了该字节码对象中的所有、构造器对象;
-
Field getDeclaredField(String name):返回一个 Field对象,根据指定的字段属性名来查找返回;
-
Method getDeclaredMethod(mName,pType...):返回一个Method方法对象,根据方法名、参数类型作为区分;
- Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):返回一个任意修饰符的Constructor对象,根据参数类型查找返回;
- java.net.URL getResource(String name):
- T cast(Object obj):将指定obj对象强制转换成Class字节码对象所代表的类。
- 代码示例
//创建字节码对象
Class c = Class.forName("com.yry.pratice.reflection.bean.User");
System.out.println(c);
//将字节码对象c换成字符串输出:Class或Interface 全类名
String s = c.toString();
System.out.println(s);
//获取字节码对象所代表类的名称
String name = c.getName();//全类名
System.out.println(name);//com.yry.pratice.reflection.bean.User
String simpleName = c.getSimpleName();//简类名
System.out.println(simpleName);//User
//由字节码对象 反射创建 实例对象s
Object instance = c.newInstance();
System.out.println(instance);//输出instance.toString()的执行结果
//判断指定obj对象是否 该字节码对象的一个实例
Object oo = new Object();
boolean b = c.isInstance(oo);
System.out.println(b);
//获取加载该字节码对象的类加载器
ClassLoader classLoader = c.getClassLoader();
System.out.println(classLoader);//
//获取该字节码对象的修饰符--public公开的修饰符
int modifiers = c.getModifiers();//不同的int值代表不同的修饰符
System.out.println(modifiers);//1
//将int类型修饰符 转换成 String字符串表示
String s1 = Modifier.toString(modifiers);
System.out.println(s1);//1对应public
1.4 java.lang.reflect.Constructor<T>构造器类
- 定义
Constructor提供关于类的单个构造方法的信息 以及对它的访问权限。
- 实质
Constructor在反射中指的是Class字节码对象所代表类的构造器。
- 常用方法
-
Class<T> getDeclaringClass():返回Class字节码对象,返回声明此Constructor构造器 表示的Class"类"对象;
-
String getName():返回此构造方法的名称;
-
int getModifiers():返回此构造方法的修饰符(需要配合Modifier类的toString()方法将其转换成字符串);
- Class<?>[] getParameterTypes():按声明顺序返回此构造方法参数列表中的 参数类型数组;
-
int getParameterCount():返回此构造方法参数列表中的 参数的个数;
- Class<?>[] getExceptionTypes():返回一组 此构造方法抛出的所有异常类型数组;
- boolean equals(Object obj):将此Constructor对象与obj对象进行比较是否相同,如果两个对象由相同的类声明并且具有相同的形参类型则是相同的。
- int hashCode():返回此Constructor的哈希码,与此Constructor所代表的类的哈希码相同;
- String toString():返回描述此构造器的字符串,构造方法唯一可能的修饰符是访问修饰符public、protected或private;
- T newInstance(Object ... initargs):通过此构造方法所代表的类来 创建的新对象;
- boolean isVarArgs():判断此构造方法是否带可变数量的参数;
- T getAnnotation(Class<T> annotation):如果此构造器上 存在指定annotation.Class类型的注释,返回这些注释;
- Annotation[] getDeclaredAnnotations():返回一组 直接存在于此构造器上的所有注释;
-
- 代码示例
//创建字节码对象
Class c = Class.forName("com.yry.pratice.reflection.bean.User");
//获取该字节码对象的构造器
Constructor[] constructors = c.getConstructors();
for (Constructor con:constructors) {
System.out.println(con.getModifiers());//构造器的修饰符
System.out.println(con.getDeclaringClass());//声明该构造器的类
System.out.println(con.getName());//构造器的名称
//获取构造器的参数类型数组
Class[] parameterTypes = con.getParameterTypes();
for (Class cas: parameterTypes) {
System.out.println(cas.getClass());//构造器参数的类型
System.out.println(cas.getSimpleName());//构造器参数的名称
}
}
1.5 java.lang.reflect.Field属性类<重要掌握>
- 定义
Field提供有关 类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类字段(静态字段)或实例字段。
- 实质
Field指的是Class字节码对象所代表的类中的普通字段或属性,每一个字段或属性都是一个对象。
- 常用方法
-
Class<?> getDeclaringClass():返回声明此Field对象所表示字段的类或接口 的Class对象;
-
String getName():返回此Field对象所表示字段的名称;
- int getModifiers():返回此Field对象所表示字段的修饰符;
- boolean isEnumConstant():判断该Filed对象所表示的字段是否是枚举类型的元素;
-
Class<?> getType():返回此Field对象所表示字段的数据类型;
- boolean equals(Object obj):判断两个字段对象obj与此Filed对象比较是否相同,如果两个对象由相同的类声明并且具有相同的名称和类型,则相同的;
- int hashCode():返回此Field对象所表示字段的哈希地址;
- String toString():返回该Field对象所表示字段的字符串表示形式(原字段的定义格式);
-
Object get(Object obj):从obj对象中获取 此Field表示的字段的值(obj对象需要拥有相同Filed表示的字段);
- boolean getBoolean(Object obj):从obj对象中 获取一个静态或实例 boolean字段的值(相同用法可以获取其他数据类型字段的值);
- void set(Object obj, Object value):将obj对象上此Field对象表示的字段 设置为指定的新值;
- T getAnnotation(Class<T> annotation):如果此字段属性上 存在指定annotation.Class类型的注释,返回这些注释;
- 代码示例
//创建字节码对象
Class c = Class.forName("com.yry.pratice.reflection.bean.User");
//获取该字节码对象的public公开的"字段对象"
Field no = c.getField("no");//仅获取public字段
System.out.println(no.getType().getSimpleName());//字段类型:int
System.out.println(no.getName());//字段名/变量名:no
//获取该字节码对象所有的"字段对象"
Field[] declaredFields = c.getDeclaredFields();
for (Field f: declaredFields) {
System.out.println(f.getModifiers());//字段修饰符
System.out.println(f.getType());//字段数据类型
System.out.println(f.getName());//字段名或变量名
}
1.6 java.lang.reflect.Method方法类<重要掌握>
- 定义
Method提供关于 类或接口上单独某个方法的信息,以及如何访问该方法。所反映的方法可能是类方法或实例方法(包括抽象方法)。
- 实质
Method指的是Class字节码对象所代表的类中的方法,即常用的自定义方法或普通方法 对象。
- 常用方法
-
Class<?> getDeclaringClass():返回声明此Method对象所表示方法的类或接口 的Class对象;
-
String getName():返回此Method对象所表示方法的名称;
- int getModifiers():返回此Method对象所表示方法的修饰符(需要配合Modifier类的toString()方法将其转换为字符串);
-
Class<?> getReturnType():返回此Method对象所表示方法的返回值类型;
-
Class<?>[] getParameterTypes():返回一组 此Method对象所表示方法的参数(类型)列表;
- int getParameterCount():返回此Method对象所表示方法的 参数个数;
- Class<?>[] getExceptionTypes()::返回一组 此Method对象所表示方法抛出的 异常类型;
- boolean equals(Object obj):判断两个方法对象 obj对象与此Method对象是否相同,两个对象代表的方法由相同的类声明 并具有相同的名称、形参类型和返回类型,则相同;
- String toString():返回该Method对象的字符串表示形式(该Method对象所代表方法的定义格式);
- boolean isVarArgs():判断此Method对象所表示方法是否有可变数量的参数;
- T getAnnotation(Class<T> annotation):如果此Method对象所表示的方法上存在指定annotation.Class类型的注释,返回这些注释;
- Annotation[] getDeclaredAnnotations():返回一组 直接存在于此Method对象所表示的方法上的所有注释。
- 代码示例
//创建字节码对象
Class c = Class.forName("com.yry.pratice.reflection.bean.User");
//获取该字节码对象的"方法对象"
Method[] methods = c.getMethods();
for (Method me:methods) {
System.out.println(me.getDeclaringClass().getSimpleName());//声明该方法的类
System.out.println(me.getModifiers());//方法的修饰符
System.out.println(me.getReturnType());//方法的返回值类型
System.out.println(me.getName());//方法的名称
Class<?>[] parameterTypes = me.getParameterTypes();//方法的参数列表
for (Class cs:parameterTypes) {
System.out.println(cs.getClass().getSimpleName());//参数类型的名称
System.out.println(cs.getName());//参数的名称
//System.out.println(cs.getSimpleName());//参数的名称
}
}
1.7 java.lang.reflect.Modifier修饰符类
- 定义
Modifier提供关于 类或接口上的字段、属性、方法的修饰符信息,Modifier类中提供的都是static方法和常量,直接使用类名.调用,可以对类和成员的访问修饰符进行解码。
- 特点
修饰符集被表示为整数,用不同的位-位置 (bit position) 表示不同的修饰符,需要使用toString(位置)方法将位置转换为字符串(真正的修饰符表示)。
- 实质
Modifier指的是Class字节码对象所代表的类中的修饰符,即常用的修饰符对象。
- 字段
- int PUBLIC = 0x00000001:表示public修饰符的 int值,getModifiers()返回 1表示该修饰符为public;
- int PRIVATE = 0x00000002:表示private修饰符的 int值,getModifiers()返回 2(以下同理:返回值是几对应不同的修饰符);
- int PROTECTED = 0x00000004:表示protected修饰符的 int值,getModifiers()返回 4;
- int STATIC = 0x00000008:表示static修饰符的 int值,getModifiers()返回 8;
- int FINAL = 0x00000010:表示final修饰符的 int值,getModifiers()返回 10;
- int SYNCHRONIZED = 0x00000020:表示synchronized修饰符的 int值,getModifiers()返回 20;
- int VOLATILE = 0x00000040:表示volatile修饰符的 int值,getModifiers()返回 40;
- int TRANSIENT = 0x00000080:表示transient修饰符的 int值,getModifiers()返回 80;
- int NATIVE = 0x00000100:表示native修饰符的 int值,getModifiers()返回 100;
- int INTERFACE = 0x00000200:表示interface修饰符的 int值,getModifiers()返回 200;
- int ABSTRACT = 0x00000400:表示abstract修饰符的 int值,getModifiers()返回 400。
- 常用方法
-
String toString(int mod):将getModifiers()返回的代表指定修饰符的int值 转换为字符串;
- boolean isPublic(int mod):判断整数参数mod是否包括public修饰符;
- boolean isXxx(int mod):判断整数参数mod是否包括Xxx修饰符。
- 代码示例
//创建字节码对象,获取字节码文件
Class c = Class.forName("com.yry.pratice.reflection.bean.User");
//获取字节码中 Filed字段对象
Field no = c.getField("no");
//获取修饰Field字段的修饰符--返回的int值代表不同的修饰符
int modifiers = no.getModifiers();
System.out.println(modifiers);//返回int值为1
//将返回的int值转换为对应修饰符的字符串表示
String s = Modifier.toString(modifiers);
System.out.println(s);//int值为1对应的修饰符为:Public