反射

一、反射

反射机制:将类的各个组成部分封装为其他对象,这就是反射机制。

反射的好处:

	* 可以在程序运行过程中,操作这些对象。
	* 可以解耦,提高程序的可扩展性。

Java代码在计算机中经历的三个阶段:

  1. Source源代码阶段:.java被编译成.class字节码文件。
  2. Class类对象阶段:*.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于在内存中描述字节码文件),Class对象将原字节码文件中的成员变量抽取出来封装成数组Field[],将原字节码文件中的构造函数抽取出来封装成数组Construction[],在将成员方法封装成Method[]。当然Class类内不止这三个,还封装了很多,我们常用的就这三个。
  3. RunTime运行时阶段:创建对象的过程new。

在这里插入图片描述

二、获取Class对象的三种方式
  1. 【Source源代码阶段】 Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中。读取文件,加载类。

  2. 【Class类对象阶段】 类名.class:通过类名的属性class获取

    多用于参数的传递。

  3. 【Runtime运行时阶段】对象.getClass():getClass()方法是定义在Objec类中的方法。

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。

三、获取对象的方法测试
package com.cx;

import com.cx.entity.Person;
import org.junit.Test;

public class test {
    @Test
    public void test01() throws ClassNotFoundException {
//        1.Class.forName("全类名")
        Class<?> clazz = Class.forName("com.cx.entity.Person");
        System.out.println(clazz);
//        2.类名.class
        Class<Person> clazz2 = Person.class;
        System.out.println(clazz2);
//        3.对象.getClass()
        Person person = new Person();
        Class clazz3 = person.getClass();
        System.out.println(clazz3);
//        比较三个对象
        System.out.println(clazz==clazz2);
        System.out.println(clazz2==clazz3);
    }
}

结果:
在这里插入图片描述

四、Class对象功能
		1. 获取成员变量们
			* Field[] getFields() :获取所有public修饰的成员变量
			* Field getField(String name)   获取指定名称的 public修饰的成员变量

			* Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
			* Field getDeclaredField(String name)  
		2. 获取构造方法们
			* Constructor<?>[] getConstructors()  
			* Constructor<T> getConstructor(<?>... parameterTypes)  

			* Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  
			* Constructor<?>[] getDeclaredConstructors()  
		3. 获取成员方法们:
			* Method[] getMethods()  
			* Method getMethod(String name,<?>... parameterTypes)  

			* Method[] getDeclaredMethods()  
			* Method getDeclaredMethod(String name,<?>... parameterTypes)  

		4. 获取全类名	
			* String getName()  
			
		--------------------------------------------------------
		* Field:成员变量
		* 操作:
			1. 设置值
				* void set(Object obj, Object value)  
			2. 获取值
				* get(Object obj) 
	
			3. 忽略访问权限修饰符的安全检查
				* setAccessible(true):暴力反射
		--------------------------------------------------------
		* Constructor:构造方法
			* 创建对象:
				* T newInstance(Object... initargs)  
				* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
		--------------------------------------------------------
		* Method:方法对象
			* 执行方法:
				* Object invoke(Object obj, Object... args)  
		
			* 获取方法名称:
				* String getName:获取方法名

1. 测试getDeclaredFields()

    @Test
    public void test02() throws Exception {
        Class<Person> person = Person.class;
//        获取所有public修饰的成员变量
        Field[] fields = person.getFields();
        for (Field field : fields) {
//         输出结果为空,因为都是private修饰的
            System.out.println(field);
        }
//        获取成员变量name的值
        Field name = person.getDeclaredField("name");
        //私有属性,暴力反射
        name.setAccessible(true);
        Person person1 = new Person();
        Object o = name.get(person1);
        System.out.println(o);
//      设置成员变量name的值
        name.set(person1,"帅");
        System.out.println(person1);
    }

运行结果:
在这里插入图片描述
这里是获取获取所有的成员变量,不考虑修饰符getDeclaredFields(),name是private修饰的,所以要进行暴力反射,其他的几个同理。

  1. 测试获取构造方法
    @Test
    public void test03() throws Exception {
        Class<Person> personClass = Person.class;
        //获取有参构造器对象
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //利用有参构造器创造对象
        Person person = constructor.newInstance("帅", 22);
        System.out.println(person);
        System.out.println("-------------------");
        //获取无参构造器
        Constructor<Person> constructor1 = personClass.getConstructor();
        //利用无参构造器创建对象
        Person person1 = constructor1.newInstance();
        System.out.println(person1);
        //无参构造器创建对象可以用这个代替
        Person person2 = personClass.newInstance();
        System.out.println(person2);
    }

结果:
在这里插入图片描述
无参构造器创建对象可以用操作可以简化:Class对象的newInstance方法。

  1. 获取成员方法们
package com.cx.entity;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void eat(){
        System.out.println("eat.......");
    }

    public void eat(String food){
        System.out.println("eat....."+food);
    }
}

	@Test
    public void test04() throws Exception {
        Class<Person> personClass = Person.class;
        //获取指定名称的空参方法
        Method eat_method1 = personClass.getMethod("eat");
        //执行方法
        Person person = new Person();
        eat_method1.invoke(person);
        System.out.println("---------------------");
        //获取指定名称的有参方法
        Method eat_method2 = personClass.getMethod("eat", String.class);
        //执行方法
        eat_method2.invoke(person,"苹果");
        //获取方法名称
        String name = eat_method2.getName();
        System.out.println(name);
    }

结果:
在这里插入图片描述

五、综合案例

需求:不改变该类代码的前提下,创建任意类的对象,执行任意的方法

  1. 创建pro.properties配置文件:
className=com.cx.entity.Person
methodName=eat
  1. 创建person和student实体
package com.cx.entity;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void eat(){
        System.out.println("eat.......");
    }

    public void eat(String food){
        System.out.println("eat....."+food);
    }
}

package com.cx.entity;

public class Student {
    public void sleep(){
        System.out.println("sleep........");
    }
}

  1. 代码执行
	@Test
    public void test05() throws Exception {
        //1.加载配置文件
        Properties properties = new Properties();
        properties.load(test.class.getClassLoader().getResourceAsStream("pro.properties"));
        //2.获取配置文件中定义的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");
        //3.加载该类进内存
        Class<?> cls = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(obj);
    }
  1. 结果
    在这里插入图片描述
  2. 修改配置文件:
className=com.cx.entity.Student
methodName=sleep
  1. 修改后运行结果
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值