一、认识反射
1.反射机制
是 Java 的特性之一 是构建框架技术的基础所在 指在运行状态中,动态获取信息以及动态调用对象方法的功能 有3个动态性质
在编译时不确定是哪个类被加载了,而是在程序运行时才加载、探知和使用 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制 能够知道类的基本结构,称为Java 类的自审 如 代码的自动提示功能,就是利用此反射的原理 使用反射,可以实现以下功能:
在运行时判断任意一个对象所属的类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的方法和属性 在运行时调用任意一个对象的方法
2.Java 反射常用API
使用 Java 反射技术常用的类如下:
class 类:反射的核心类
反射所有的操作都是围绕该类来生成的 通过此类可以获得类的属性、方法等内容信息 Field 类:表示类的属性
Method 类:表示类的方法
Constructor 类:表示类的构造方法
在 Java 程序中使用反射的基本步骤如下
导入 java.lang.reflect.* 获得需要操作的类的 Java.lang.Class 对象 调用 Class 的方法获取 Field,Method 等对象 使用反射API进行操作
二、反射的应用
1.获取类的信息
获取 Class 对象
调用对象的 getClass() 方法
是 java.lang.Object 类中的一个方法 该方法返回该对象所属类对应的 Class 对象 Student stu = new Student(); Class cla = stu.getClass(); 调用类的 class 属性
可获取该类对应的 Class 对象 需要在编译期间就知道类的名称 Class cla = Student.class; 使用 Class 类的 forName() 静态方法
需要传入字符串参数 该字符串参数的值是某个类的全名 即要在类名前添加完整的包名 如果传入的字符串不是类的全名,就会抛出一个 ClassNotFoundException Class cla = Class.forName(“com.tttest.reflection.Student”); 调用某个类的 class 属性来获取该类对应的 Class 对象这种方式更有优势
代码更安全 :在编译阶段就可以检查出需要访问的 class 对象是否存在 程序性能更高 :无需调用方法,性能更好 通过 Class 对象获取信息
在获得某个类所对应的 Class 对象之后 程序就可以调用 Class 对象的方法类获取该类的详细信息 Class 类提供了大量实例方法来获取 Class 对象所对应的详细信息
方法 说明 Constructor getConstructor(Class[] params) 返回此 Class 对象所包含的类的指定的 public 构造方法,params 参数是按声明顺寻指定该方法参数类型的 Class 对象的一个数组。构造方法的参数类型与 params 所指定的参数类型匹配 Constructor[] get Constructors() 返回此 Class 对象所包含的类的所有 public 构造方法 Constructor getDeclaredConstructor(Class[] params) 返回此 Class 对象所包含的类的指定构造方法,与构造方法的访问级别无关 Constructor[] getDeclaredConstructors() 返回此 Class 对象所包含的类的所有构造方法,与构造方法访问级别无关
方法 说明 Method getMethod(String name, Class[] params) 返回此 Class 对象所包含的类的指定的 public 方法, name 参数用于指定方法名称, params 参数是按声明顺序标志该方法参数类型的 Class 对象的一个数组 Method[] getMethods 返回此 Class 对象所包含的类的所有 public 方法 Method getDeclaredMethod(String name,Class[] params) 返回此 Class 对象所包含的类的指定方法,与方法的访问级别无关 Method[] getDeclaredMethods() 返回此 Class 对象所包含的类的全部方法,与方法的访问级别无关
方法 说明 Field getField(String name) 返回此 Class 对象所包含的类的指定的 public 属性,name 参数用于指定的属性名称 Field[] getFields() 返回此 Class 对象所包含的类的所有 public 属性 Field getDeclaredField(String name) 返回此 Class 对象所包含的类的指定属性,与属性的访问级别无关,name 表示属性名称 Field[] getDeclaredFields() 返回此 Class 对象所包含的类的全部属性,与属性的访问级别无关
方法 说明 <A extends Annotation>A getAnnotation(Class<A> annotationClass) 试图获取该 Class 对象所表示类上指定类型的注解,如果该类型的注解不存在则返回 null ,其中 annotationClass 参数到底英语注解类型的 Class 对象 Annotation[] getAnnotations() 返回此类上存在的所有注解 Annotation[] getDeclaredAnnotations() 返回直接存在于此类上的所有注解
方法 说明 Class[] getDeclaredClasses() 返回该 Class 对象所对应类里包含的全部内部类 Class[] getDeclaredClass() 返回 Class 对应的类所在的外部类 Class[] getInterfaces() 返回该 Class 对象对应类所实现的全部接口 int getModifiers() 返回此类或接口的所有修饰符,返回的修饰符由 public、protected、private、final、static、abstract等对应的常量组成,返回的整数应使用 Modifer 工具类的方法来解码,才可以获取真实的修饰符 Package getPackage() 获取此类的包 String getName() 以字符串形式返回此 Class 对象所表示的类的名称 String getSimpleName() 以字符串形式返回此 Class 对象所表示的类的简称 Class getSuperclass() 返回该 Class 所表示的类的超类对应的 Class 对象
Class 对象可以获得该类里的成员,包括方法,构造方法及属性。 Method\Constructor\Field这3个类都定义在 java.lang.reflect 包下 实现了 java.lang.reflect.Member 接口
通过 Method 对象来执行对应的方法 通过 Constructor 对象来调用相应的构造方法创建对象 通过 Field 对象直接访问并修改对象的属性值
2.创建对象
通过反射来创建对象有如下两种模式:
使用 Class 对象的 newInstance() 方法创建对象
要求该 Class 对象的对应类由默认构造方法 而执行 newInstance() 方法时实际上是利用默认构造方法来创建该类的实例
import java. util. Date ;
public class ReflectTest {
public static void main ( String [ ] args) {
Class cla= Date . class ;
try {
Date d= ( Date ) cla. newInstance ( ) ;
System . out. println ( d. toString ( ) ) ;
} catch ( Exception e) {
e. getStackTrace ( ) ;
}
}
}
使用 Constructor 对象创建对象
先使用 Class 对象获取指定的 Constructor 对象 再调用 Constructor 对象的 newInstance() 方法创建该 Class 对象对应类的示例 可以选择使用某个类的指定构造方法来创建实例
import java. lang. reflect. Constructor ;
import java. util. Date ;
public class ReflectTest {
public static void main ( String [ ] args) {
Class cla= Date . class ;
try {
Constructor cu= cla. getConstructor ( long . class ) ;
Date d= ( Date ) cu. newInstance ( 1997 ) ;
System . out. println ( d. toString ( ) ) ;
} catch ( Exception e) {
e. getStackTrace ( ) ;
}
}
}
3.访问类的属性
使用 Field 对象可以获取对象的属性 通过 Field 对象可以对属性进行取值或赋值操作
方法 说明 Xxx getXxx(Object obj) 该方法中 Xxx 对应8个基本数据类型,obj为该属性所在的对象 Object get(Object obj) 得到引用类型属性值 void setXxx(Object obj,Xxx val) 将 obj 对象的该属性设置成 val 值,此处的 Xxx 对应8个基本数据类型 void set(Object obj, object val) 将 obj 对象的该属性设置成 val 值,针对引用类型赋值 void setAccessible(bool flag) 对获取到的属性设置访问权限,参数为true,可以对私有属性取值和赋值
4.访问类的方法
使用Method 对象可以调用对象的方法
Object invoke(Object obj,Object args)
obj 是执行该方法的对象 args 是执行该方法时传入该方法的参数
import java. lang. reflect. * ;
public class ReflectTest {
public static void main ( String [ ] args) {
Class cla= Student . class ;
Student stu= new Student ( ) ;
try {
Method met1= cla. getMethod ( "setName" , String . class ) ;
met1. invoke ( stu, "Bobby" ) ;
Method met2= cla. getMethod ( "getName" , null ) ;
Object obj= met2. invoke ( stu, null ) ;
System . out. println ( obj) ;
} catch ( Exception e) {
e. getStackTrace ( ) ;
}
}
}
当通过 Method 的 invoke()方法调用对应的方法时,Java 会要求程序必须有调用该方法的权限 如果程序确实需要调用某个对象的 private 方法,可以先调用 setAccessible() 方法
将 Method 对象的 accessible 标志设置为指示的布尔值 值为 true 则表示该 Method 在使用时应该取消 Java 语言访问权限检查 值为 false 则表示该 Method 在使用时应该进行 Java 语言访问权限检查
5.使用 Array 类动态创建和访问数组
程序可以通过使用 Array 类来动态地创建数组、操作数组元素等
import java. lang. reflect. * ;
public class ReflectTest {
public static void main ( String [ ] args) {
Object arr= Array . newInstance ( String . class , 5 ) ;
Array . set ( arr, 2 , "Jack" ) ;
Array . set ( arr, 3 , "Rose" ) ;
Object o1= Array . get ( arr, 2 ) ;
Object o2= Array . get ( arr, 3 ) ;
System . out. println ( o1) ;
System . out. println ( o2) ;
}
}
注意:
通过反射创建对象时性能要稍微低一些 只有当程序需要动态创建某个类的对象时才会考虑使用反射 通常在开发通用性比较广的框架、基础平台时可能回大量使用反射