更多内容,前往IT-BLOG
反射Reflection
被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API
取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。如果仅对设计应用程序感兴趣,而对构造工具不感兴趣,就没有学习的必要。
一、反射机制提供的功能(什么时候会用到反射)
【1】在运行时判断任意一个对象所属的类。
【2】在运行时构造任意一个类的对象。
【3】在运行时判断任意一个类所具有的成员变量和方法。
【4】在运行时调用任意一个对象的成员变量和方法。
【5】生成动态代理。
二、Class 类
在程序运行期间,Java
运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。保存这些信息的类被称为Class
,Object
类中的getClass()
方法将会返回一个Class
类型的实例。具体细节说明如下:
public class ReflecTest {public static void main(String[] args) throws Exception {// Class 是带泛型的/*如果一个 Person 对象表示一个特定的人,那么一个 Class 对象将表示一个特定类的属性。*///获取 Class 的方法有三种,第一种如下:Class<Person> clazz = Person.class;//创建 clazz 对应的运行时类 Person 类的对象//创建类的对象:调用Class对象的newInstance()方法:要求是类必须有一个无参的构造器和类的构造器的访问权限需要足够Person person = clazz.newInstance();//也可以先 new 一个对象,通过 getClass 获取 Class对象,从而得知此目标对象的结构Person personTest = new Person();//获取 Class 的方法有三种,第二种如下:Class<? extends Person> personTestClass = personTest.getClass();String clazzName = personTestClass.getName();//com.reflection.learn.PersonSystem.out.printf(clazzName);//获取 Class 的方法有三种,第三种如下:通过Class 的静态方法获取Class<?> className = Class.forName("com.reflection.learn.Person");//通过反射调用运行时类的指定属性Field name = clazz.getField("name");//给属性设置set 时,需要将**对象**和值传入 信息定义 public String name;//获取公有方法name.set(person,"Li Si");//获取私有方法 private int age;Field age = clazz.getDeclaredField("age");age.setAccessible(true);age.set(person,20);//通过反射指定运行的方法(无参)Method display = clazz.getMethod("display");//执行此方法invoke,需要传入对象display.invoke(person);//如果是静态方法,调用规则如下://display.invoke(Person.class);//调用有参方法 public void getObject(String name)Method getObject = clazz.getMethod("getObject", String.class);//方法的执行,需传入对象和参数getObject.invoke(person,"Li Si");}
}
java.lang.Class
:是反射的源头:我们创建一个类,通过编译器javac.exe
生成对应的.class
文件。之后使用java.exe
加载(JVM 的类加载器)此.class
文件到内存中以后,此文件就是一个运行时类,存在于缓冲区中。这个运行时类本身就是一个Class
是实例。且一个运行时类只加载一次。
三、通过反射调用类中指定的方法和属性(重要)
@Test
public void test2() throws Exception{//获取指定的构造器Class<Person> clazz = Person.class;//获取指定构造器Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);Person person = declaredConstructor.newInstance("li", 20);//输出:Person{name='li', age=20}System.out.println(person.toString());//获取指定的方法Method getObject = clazz.getDeclaredMethod("getObject", String.class);getObject.invoke(person,"zhangsan");
}
四、利用反射分析类的能力
在java.lang.reflect
包中有三个类Field
(域)、Method
(方法)、Constructor
(构造器)。这三个类都有一个叫做 getName
方法,用来返回项目的名称。Filed
类有一个getType
方法,用来返回描述域所属类型的 Class 对象。Method 与 Constructor 类有能够报告参数的方法getParameterTypes
,Method
类还有一个可以报告返回类型的方法getReturnType()
。这三个类还有一个getModifiers
方法,返回一个整数值,用不同的位开关描述public
和private
等修饰符。可以通过Modifier
类的静态方法将整数转化为public
等。Class
类的getDeclareFields
、getDeclareMethods
和 getDeclaredConstructors
方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括父类成员。
get
方法有一个需要解决的问题,name
域是一个String
时,我们将它当做Object
返回没有什么问题。但是假定我们想要查看salary
域。它属于double
类型,而java
中数据值类型不是对象。可以使用Field
类中的getDouble
方法,也可以调用get
方法,此时,反射机制将自动地将这个域值打包到相应的对象包装器中,这里打包成Double
。
五、应用
【1】JDK 的动态代理:链接
【2】Spring 的 AOP 面向切面编程:链接