概念
官方介绍
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
通俗解释
把.class文件加载到内存,并创建对应的Class对象(这是第一步),然后获取Class对象(字节码对象)中的Constructor,Field,Method(这是第二步)。并通过这三个对象任意操作内存中的对象属性和行为(这是第三步)。
名词解释:
- Constructor 构造方法描述器
- Field 属性描述器
- Method 方法描述器
获取Class对象的方式
当Person.class或者Dog.class文件加载到内存后可以得到一个对象,这个对象叫做Class,我们称为字节码对象,.class文件我们称为字节码文件,下面介绍如何把.class文件加载到内存,并得到对应的Class对象
public class 获取Class对象的三种方式 {
public static void main(String[] args) throws Exception {
//1.类名.class
Class clazz = Person.class;
//2.Class.forName("全类名");
Class clazz2 = Class.forName("cn.itcast.domain.Person");
//3.对象名.getClass()
Class clazz3 = new Person().getClass();
System.out.println(clazz==clazz2);//true
System.out.println(clazz3==clazz2);//true
}
}
输出都是true,说明同一个.class文件被加载到内存后我们得到的Class对象都是一样的
通过反射操作对象的属性
案例一:通过反射给person对象的name属性赋值,并且在获取name属性的值
Person person = new Person("李四", 13);
/*1.获取字节码对象*/
Class clazz = Person.class;
//2.获取name属性对应的Field对象
Field field = clazz.getDeclaredField("name");
//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
field.setAccessible(true);
//4.通过field给person对象的name属性赋值,等价于 person.name = "张三";
field.set(person, "张三");
//5.通过field获取person对象的name属性值,等价于 String name = person.name;
String name = (String)field.get(person);
//测试输出结果
System.out.println(name);//输出张三
通过反射操作构造方法
案例一:获取public Person(String name, int age){}构造方法对应的Constructor并创建对象
//1.获取字节码对象
Class clazz = Person.class;
//2.获取Person(String, int)构造对应的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
constructor.setAccessible(true);
//4.通过constructor创建对象,等价于 Person person = new Person("张三",13);
Person person = (Person)constructor.newInstance("张三", 13);
System.out.println(person);
案例二:获取public Person(int age, String name){}构造方法对应的Constructor并创建对象
//1.获取字节码对象
Class clazz = Person.class;
//2.获取Person(int age, String name)构造对应的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor(int.class,String.class);
//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
constructor.setAccessible(true);
//4.通过constructor创建对象,等价于 Person person = new Person(13,"张三");
Person person = (Person)constructor.newInstance(13,"张三");
System.out.println(person);
案例三:获取Person()构造方法对应的Constructor并创建对象
//1.获取字节码对象
Class clazz = Person.class;
//2.获取Person()构造对应的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor();
//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
constructor.setAccessible(true);
//4.通过constructor创建对象,等价于 Person person = new Person();
Person person = (Person)constructor.newInstance();
System.out.println(person);
通过反射操作对象的普通方法
案例一:通过反射调用person对象的public setName(String name){}方法
Person person = new Person("张三", 13);
//1.获取字节码对象
Class clazz = Person.class;
//2.获取public setName(String name){} 方法对应的Method对象
Method setName = clazz.getDeclaredMethod("setName",String.class);
//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
setName.setAccessible(true);
//4.执行person对象的setName方法,等价于person.setName("李四");
setName.invoke(person,"李四");
System.out.println(person);
案例二:通过反射调用person对象的public getName(){}方法
Person person = new Person("张三", 13);
//1.获取字节码对象
Class clazz = Person.class;
//2.获取public String getName(){} 方法对应的Method对象
Method getName = clazz.getDeclaredMethod("getName");
//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
getName.setAccessible(true);
//4.执行person对象的getName方法,等价于String name = person.getName("李四");
String name = getName.invoke(person);
System.out.println(name);
反射案例
通过反射执行配置文件中指定类的指定方法
在src根目录新建配置文件config.properties,内容如下:
className=cn.itcast.domain.Student
methodName=sleep
Student类代码,该类必须放在cn.itcast.domain包下
public class Student{
public void sleep(){
System.out.println("睡觉");
}
}
测试代码如下:
Properties properties = new Properties();
//表示将src目录下的config.properties中的数据加载到当前properties集合中
properties.load(反射的应用真实案例.class.getClassLoader().getResourceAsStream("config.properties"));
/*目的:执行className对应的类中methodName对应的方法*/
/*换句话说目的就是:执行Student类中sleep()方法*/
/*1.根据配置文件中className的值将Student类加载到内存,得到Class字节码对象*/
String className = properties.getProperty("className");
Class clazz = Class.forName(className);
/*2.通过Class字节码对象得到Constructor对象,并创建Student对象*/
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object stu = constructor.newInstance();
/*3.通过Class字节码对象和methodName的值得到Method对象,并执行stu对象的方法*/
String methodName = properties.getProperty("methodName");//methodName的值sleep
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(stu);
好处
通过反射创建的这个测试类代码通用性非常强,可以执行任意类的任意无参方法。好处就是把项目部署到服务器以后如果想执行其他类的方法,只需要修改服务器上的config.properties配置文件即可。如果按照以前的方式:new Student().sleep()
,想执行其他类的方法那么必须要在开发者电脑上修改源代码,重新编译项目,最后还需要重新上传到服务器,非常麻烦。