【Java学习】Java反射机制以及实现简单框架(暴力反射)

Java反射机制:JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制

反射的好处:

      1.可以在程序运行过程中,操作这些对象。

      2.可以进行解耦,提高程序的扩展性。

  • Java代码在计算机中的三个阶段

1. Sources源代码阶段:*.java被编译成*.class字节码文件。

2.Class类对象阶段:*.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于描述在内存中描述字节码文件),

Class对象将原字节码文件中的成员变量,构造函数,方法等的做了封装。

3.Runtime运行阶段:创建对象的过程new

  •  讲解获取Class对象的方式

获取Class对象的三种方式对应着java代码在计算机中的三个阶段:

  • 源代码阶段

Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。

  • Class类对象阶段

类名.class:通过类名的属性class获取

  • Runtime运行时阶段

对象.getClass():getClass()方法是定义在Object类中的方法。

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

测试代码:

package com.company.reflect;

import com.company.reflect.domain.Person;

/**
 * ⊙﹏⊙&&&&&&⊙▽⊙
 *
 * @Auther: pangchenbo
 * @Date: 2020/5/9 10:37
 * @Description:
 */
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一:Class.forName("全类名")
        Class<?> aClass = Class.forName("com.company.reflect.domain.Person");
        System.out.println(aClass);
        //方式二:类名.class
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
        //方式三:对象.getClass()
        Person person = new Person();
        Class<? extends Person> aClass1 = person.getClass();
        System.out.println(aClass1);
        //比较 == 三个对象
        System.out.println(aClass == aClass1);
        System.out.println(personClass==aClass1);
    }
}

两个true表示Class对象是同一个。

  •  获取Class对象功能

(1)获取成员变量们
		Field[] getFields() :获取所有public修饰的成员变量
		Field getField(String name)   获取指定名称的 public修饰的成员变量

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

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

		Method[] getDeclaredMethods()  
		Method getDeclaredMethod(String name, 类<?>... parameterTypes)
  •  Field:成员变量

先写一个测试类

public class Person {
    private String name;
    private int age;
    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {

    }

    public Person(String name, Integer 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 +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
    //无参方法
    public void eat(){
        System.out.println("eat...");
    }

    //重载有参方法
    public void eat(String food){
        System.out.println("eat..."+food);
    }
}
  • 获取所有的public修饰的成员变量
//0.获取Person对象
        Class<Person> personClass = Person.class;
        //1.获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

 

  • 获取特定的成员变量(public)
//2.Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a 的值 [也只能获取公有的,获取私有的或者不存在的字符会抛出异常]
        Person person = new Person();
        Object o = a.get(person);
        System.out.println("o  value: "+o);
        //设置属性a的值
        a.set(person,"haha");
        System.out.println(person);

 

  • 获取全部的成员变量
//Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField+" ");
        }
        System.out.println("==============================");

 

  • 获取特定的成员变量,在这里如果需要对private进行修改,就必须进行暴力反射,将
d.setAccessible(true);设置为true
System.out.println("==============================");
        Field d = personClass.getDeclaredField("d");
        d.setAccessible(true);//暴力反射
        d.get(person);
        d.set(person,"222");
        System.out.println(person);

  •  普通方法获取

获取指定名称的方法(不带参数的获取)

Class<Person> personClass = Person.class;
        //获取指定名称的方法
        Method eat = personClass.getMethod("eat");
        Person person = new Person();
        eat.invoke(person);//执行方法

 

获取指定名称的方法(带参数获取)

//获取具有参数的构造方法
        Method eat1 = personClass.getMethod("eat", String.class);
        eat1.invoke(person,"fans");
        System.out.println("===============================");

 

获取方法列表

Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);//继承的方法也会被访问(前提是方法是public)
        }

 

 如果设置的方法中含有私有的方法,也可以设置d.setAccessible(true);设置为true,然后就可以访问私有方法。

  • 构造方法

  • 获取无参数的构造器
Class<Person> personClass = Person.class;
        //Constructor<?>[] getConstructors()
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        //Constructor<T> getConstructor(类<?>... parameterTypes)
        //获取无参
        Constructor<Person> constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
            //利用获取的构造器创建对象
        Person person = constructor1.newInstance();
        System.out.println(person);

 

  • 获取有参数的构造器
//获取有参
        Constructor<Person> constructor = personClass.getConstructor(String.class,Integer.class);
        System.out.println(constructor);
        Person person1 = constructor.newInstance("PCB",100);
        System.out.println(person1);
        //理应Class类对象进行对象的构建获取
        Person person2 = personClass.newInstance();
        System.out.println(person2);
        //对于getDeclaredConstructor方法和getDeclaredConstructors方法,此外在构造器的对象内也有setAccessible(true);方法,并设置成true就可以操作了。

  •  设计简单的框架,理解反射的好处

准备测试类

package com.company.reflect.domain;

/**
 * ⊙﹏⊙&&&&&&⊙▽⊙
 *
 * @Auther: pangchenbo
 * @Date: 2020/5/9 13:27
 * @Description:
 */
public class Student {
    public void sleep(){
        System.out.println("sleep...");
    }
}

 准备文件properties文件

className = com.company.reflect.domain.Student
methodName = sleep
  •  需求

写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。

  • 实现

(1)配置文件 (2)反射

  • 步骤

(1)将需要创建的对象的全类名和需要执行的方法定义在配置文件中 (2)在程序中加载读取配置文件 (3)使用反射技术来加载类文件进内存 (4)创建对象 (5)执行方法

 

package com.company.reflect.反射案例;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * ⊙﹏⊙&&&&&&⊙▽⊙
 *
 * @Auther: pangchenbo
 * @Date: 2020/5/9 13:30
 * @Description:
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        /**
         * 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */
        //1.加载配置文件
        //1.1创建Properties对象
        Properties properties = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件  使用类加载器
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        properties.load(resourceAsStream);
        //2.获取配置文件中定义的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //加载类到内存中
        Class<?> aClass = Class.forName(className);
        //创建对象
        Object o = aClass.newInstance();
        //获取对象方法
        Method method = aClass.getMethod(methodName);
        //执行方法
        method.invoke(o);

    }
}

改变配置文件

className = com.company.reflect.domain.Person
methodName = eat

  • 好处

我们这样做有什么好处呢,对于框架来说,是人家封装好的,我们拿来直接用就可以了,而不能去修改框架内的代码。但如果我们使用传统的new形式来实例化,那么当类名更改时我们就要修改Java代码,这是很繁琐的。修改Java代码以后我们还要进行测试,重新编译、发布等等一系列的操作。而如果我们仅仅只是修改配置文件,就来的简单的多,配置文件就是一个实实在在的物理文件。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章