7.2, learn JAVA reflection learning together (super detailed)

1 What is reflection?

Reflection (reflection) is one of the characteristics of the Java program development language, which allows the running Java program to check itself, or "self-examination", also known as "introspection".
Reflection is so powerful that it can even manipulate private properties of programs directly. There is a concept in our previous study that resources encapsulated by private can only be accessed inside the class, not outside, but this rule is blatantly broken by reflection.
Reflection is like a mirror. It can get all the information of a class at runtime, get any defined information (including member variables, member methods, constructors, etc.), and can manipulate the fields, methods, constructors, etc. of the class part.

2 Why do you need reflection?

If we want to create an object, we directly new User(); Isn't it very convenient, why do we need to create an object through reflection?

Then I have to ask you a question first, why do you go to a restaurant to eat?
For example: we want to have a steak dinner, if we create our own, we have to manage everything.
The advantage is that I have a clear idea of ​​what to do every step of the way. The disadvantage is that I have to realize everything by myself, so I am not exhausted. You are in charge of cow delivery, what you eat, slaughter, transport, refrigeration, cooking, and serving. Take cooking as an example, can you have a super chef do it well?
then what should we do? There is a saying that professional things should be done by professionals, breeding by farmers, slaughter by executioners, and cooking by special chefs. What are we doing then?
Let's raise our legs and take it directly to eat.
What's more, the restaurant has finished the food, so we can't throw it on the ground. Let's pick it up and eat it. Isn't that all primitive people? then what should we do? It's very simple, put ready-made things in a container, such as steak on a plate.

In the following study, we will learn the framework. There is a framework, Spring, which is a very professional and powerful product, which can help us create and manage objects. In the future, I don't need to manually new the object, just get it directly from the Beans in the container provided by Spring. The bottom layer of Beans is actually a Map<String, Object>, which is finally obtained through getBean("user"). The core implementation of this is the use of reflection technology.

To sum up, the class is not created by you, but by your colleagues or directly by a third-party company. At this time, if you want to call the underlying functions of this class, you need reflection technology to implement it. It's a bit abstract, don't worry, let's make a case, and you will understand immediately.

3 APIs needed for reflection

3.1 Get the bytecode object
Class.forName("full path of the class");
class name.class
object.getClass();

3.2 Commonly used methods
to obtain the package name and class name

clazz.getPackage().getName()//package name
clazz.getSimpleName()//class name
clazz.getName()//complete class name

获取成员变量定义信息

getFields()//Get all public member variables, including inherited variables
getDeclaredFields()//Get member variables defined by this class, including private, but not inherited variables
getField(variable name)
getDeclaredField(variable name)

获取构造方法定义信息

getConstructor(parameter type list)//Get public constructors
getConstructors()//Get all public constructors
getDeclaredConstructors()//Get all constructors, including private
getDeclaredConstructor(int.class,String.class)

获取方法定义信息

getMethods()//Get all visible methods, including inherited methods
getMethod(method name, parameter type list)
getDeclaredMethods()//Get methods defined in this class, including private, excluding inherited methods
getDeclaredMethod(method name, int.class,String.class)

反射新建实例

clazz.newInstance();//Execute no-parameter construction to create objects
clazz.newInstance(666,"SpongeBob SquarePants");//Execute parameter-containing construction to create objects
clazz.getConstructor(int.class,String.class)//Get construction method

反射调用成员变量

clazz.getDeclaredField(variable name);//Get variable
clazz.setAccessible(true);//Make private members allow access
f.set(instance, value);//Assign a value to the variable of the specified instance, static variable, first parameter Give null
f.get(instance);//Access the value of the specified instance variable, static variable, the first parameter is null

反射调用成员方法

Method m = Clazz.getDeclaredMethod(method name, parameter type list);
m.setAccessible(true);//Make the private method allow to be called
m.invoke(instance, parameter data);//Let the specified instance execute the method

4 Application of reflection

4.1 Create: Test material class
Create package: cn.tedu.reflection
Create class: Student.java*

package cn.tedu.review;
/*本类用于复习反射的物料类*/
public class Student {
    
    
    //1.定义成员变量
    private String name;
    public int age;

    //2.给被封装属性提供get与set方法
    public String getName() {
    
    
        return name;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }

    //3.生成本类的无参构造与全参构造
    public Student(){
    
    }
    public Student(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }
    //4.提供本类的普通方法
    public void play(){
    
    
        System.out.println("今天大结局,放学后我要写1W行代码玩玩~");
    }
    public void sunDay(int n){
    
    
        System.out.println("国庆一共放"+n+"天");
    }
    //5.为了查看学生对象的具体属性与属性值,重写toString()
    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4.2 Exercise: Get a class object

Create package: cn.tedu.reflection
Create class: TestReflect.java

package cn.tedu.reflection;

import org.junit.Test;

import java.lang.reflect.Method;
import java.util.Arrays;

/*本类用于反射的测试*/
public class TestReflect {
    
    
    //1.创建程序的入口函数main()--不用
    /*单元测试方法:是Java中最小的测试单位,使用灵活,推荐指数:5颗星
    * 语法要求:@Test + public + void + 没有参数
    * 注意:使用时需要导包:Add JUnit 4 library to the build path
    * 导包后的效果:import org.junit.Test
    * 执行方式:选中方法名前绿色的小三角,成功运行会有绿色的小对勾
    * */
    //2.通过单元测试方法,获取目标类Student对应的字节码对象
    @Test
    public void getClazz() throws ClassNotFoundException {
    
    
        //练习获取字节码对象的3种方式
        Class<?> clazz1 = Class.forName("cn.tedu.review.Student");
        Class<?> clazz2 = Student.class;
        Class<?> clazz3 = new Student().getClass();

        //打印的是Student类对应的字节码对象
        System.out.println(clazz1);//class cn.tedu.reflection.Student
        //获取当前字节码对象clazz1的名字
        System.out.println(clazz1.getName());//cn.tedu.reflection.Student
        //通过字节码对象,获取Student类的类名
        System.out.println(clazz2.getSimpleName());
        //通过字节码对象,获取Student类对应的包对象
        System.out.println(clazz3.getPackage());
        //通过字节码对象,先获取Student类对应的包对象,再获取这个包对象的名字
        System.out.println(clazz3.getPackage().getName());
    }

4.3 Exercise: Get Member Variables

package cn.tedu.reflection;

import java.lang.reflect.Field;
import org.junit.Test;

/**本类用来测试反射*/
public class TestReflect {
    
    
	//3.通过单元测试方法练习引用类型数组的定义与遍历
    @Test
    public void getStu() {
    
    
        //1.创建Student类的3个对象
        Student s1 = new Student("张三", 3);
        Student s2 = new Student("李四", 4);
        Student s3 = new Student("王五", 5);
        //2.创建数组将刚刚的3个对象存入数组中
        Student[] s = {
    
    s1, s2, s3};
        //3.直接打印数组,查看数组中的元素
        System.out.println(Arrays.toString(s));
        //4.遍历学生数组,拿到每一个学生对象,做进一步的操作
        for (Student stu : s) {
    
    
            //System.out.println(stu);
            stu.play();//通过遍历到的对象,执行play()
            System.out.println(stu.age);//通过遍历到的对象,打印age属性
        }
    }

	//4.通过单元测试方法,获取Student类中的成员变量
    @Test
    public void getFie() throws ClassNotFoundException {
    
    
        //1.获取字节码对象
        Class<?> clazz = Class.forName("cn.tedu.review.Student");
        //2.通过字节码对象获取成员变量们
        Field[] fs = clazz.getFields();
        //3.遍历数组,获取每个成员变量的具体信息
        /*注意!目前成员变量的修饰符必须是public的才能获取到,不然,像默认修饰符也是获取不到的*/
        for(Field f : fs){
    
    
            System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
            System.out.println(f.getType());//通过本轮循环到的字段对象获取字段的类型
        }

    }
}

4.4 Exercise: Get the member method of the class through the bytecode object

package cn.tedu.reflection;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.junit.Test;

/**本类用来测试反射*/
public class TestReflect {
    
    
    //5.通过单元测试方法,获取Student类中的成员方法
    @Test
    public void getFunction() {
    
    
        //1.获取字节码对象
        Class<?> clazz = Student.class;
        //2.通过字节码对象获取目标类中的成员方法们
        Method[] ms = clazz.getMethods();
        //3.通过高效for循环遍历数组,拿到每一个方法对象
        for (Method m : ms) {
    
    
            System.out.println(m);//直接打印遍历到的方法对象
            System.out.println(m.getName());//通过方法对象获取方法名
            Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组
            System.out.println(Arrays.toString(pt));//打印方法参数的数组
        }

    }

4.5 Exercise: Get the constructor of a class through a bytecode object

package cn.tedu.reflection;

import java.lang.reflect.Constructor;
import java.util.Arrays;

import org.junit.Test;

/**本类用来测试反射*/
public class TestReflect {
    
    
    //6.通过单元测试方法,获取Student类中的构造方法
    @Test
    public void getCons() {
    
    
        //1.获取字节码对象
        Class<?> clazz = new Student().getClass();
        //2.通过字节码对象获取目标类Student的构造方法们
        Constructor<?>[] cs = clazz.getConstructors();
        //3.通过高效for循环遍历数组
        for(Constructor c : cs){
    
    
            System.out.println(c.getName());//打印本轮遍历到的构造方法的名字
            Class[] pt = c.getParameterTypes();//通过本轮遍历到的构造函数对象获取构造函数的参数类型
            System.out.println(Arrays.toString(pt));//打印参数类型
        }
    }

4.6 Exercise: Creating Objects

package cn.tedu.reflection;

import java.lang.reflect.Constructor;
import org.junit.Test;

/**本类用来测试反射*/
public class TestReflect {
    
    
//7.通过单元测试方法,创建Student目标类的对象
    @Test
    public void getObject() throws Exception {
    
    
        //1.获取字节码对象
        Class<?> clazz = Student.class;
        //2.通过反射技术创建目标类的对象,注意抛出异常
        /*反射创建对象方案1:通过触发目标类的无参构造创建对象*/
        Object o = clazz.newInstance();
        System.out.println(o);//这一步已经获取到了对象Student{name='null', age=0}

        /*反射创建对象方案2:通过触发目标类的全参构造创建对象
        * 思路:
        * 1.先获取指定的构造函数对象,注意需要指定构造函数的参数,传入的是.class字节码对象
        * 2.通过刚刚获取到的构造函数对象创建Student目标类的对象,并且给对象的属性赋值
        * */
        //3.获取目标类中指定的全参构造
        Constructor<?> c = clazz.getConstructor(String.class, int.class);
        //System.out.println(c);
        //4.通过获取到的构造函数:创建对象+给对象的属性赋值
        Object o2 = c.newInstance("赵六", 6);
        System.out.println(o2);
    }
}

4.7 Familiar with the API

Create a class exercise by yourself, get all the resources in the class, and get familiar with the API involved in reflection

5 violent reflexes

It means that private properties or methods in the program can be used to violently obtain resources through reflection technology. Common methods that need to be used are as follows:

insert image description here

5.1 Creation: Test Material Class

Create package: cn.tedu.reflection
Create class: Person.java*

package cn.tedu.review;
/*本类用作暴力反射测试的物料类*/
public class Person {
    
    
    //1.提供私有属性
    private String name;
    private int age;

    //2.提供私有方法
    private void save(int n,String s){
    
    
        System.out.println("save()..."+n+s);
    }
    private void update(){
    
    
        System.out.println("update()...");
    }
}

5.2 Exercise: Create a test class

Create package: cn.tedu.reflection
Create class: TestReflect2.java

package tedu.reflection;

import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/*本类用于测试暴力反射*/
public class TestReflect2 {
    
    
   /*1.通过暴力反射获取与操作属性*/
    @Test
    public void getFie2() throws Exception {
    
    
        //1.获取字节码对象
        Class<?> clazz = Person.class;
        //2.获取指定的私有属性,传入的是属性名,注意抛出异常
        Field field = clazz.getDeclaredField("name");
        //3.根据刚刚获取到的属性对象,查看属性的信息
        System.out.println(field);//直接打印获取到的字段对象
        System.out.println(field.getType().getName());//java.lang.String
        System.out.println(field.getType());//class java.lang.String

        //4.设置属性的值
        //4.1 需要指定到底是给哪个对象的name属性设置值,没有对象就创建对象
        Object obj = clazz.newInstance();//触发无参构造利用反射创建对象

        //4.2暴力反射,需要设置私有可见权限!!!
        field.setAccessible(true);

        //4.3通过字段对象给刚刚创建好的对象obj设置属性值为海绵宝宝
        //field就是我们刚刚获取的name属性
        //set(m,n)--m是给哪个对象的name属性设置值,n是设置的值是什么
        field.set(obj,"海绵宝宝");
        //4.4 打印查看刚刚设置的属性值
        //field.get(m)--field代表的就是Person类的name属性,m是查看哪个对象的这个属性值
        System.out.println(field.get(obj));
    }

    //2.定义单元测试方法,利用暴力反射操作Person类中的私有属性age【巩固练习】
    @Test
    public void getFie3() throws Exception {
    
    
        //1.获取字节码对象
        Class<?> clazz = Person.class;
        //2.获取指定的私有属性对象
        Field f = clazz.getDeclaredField("age");
        //3.根据获取到的属性对象,查看相关信息,比如属性的类型
        System.out.println(f.getType().getName());
        //4.操作:设置属性的值:一共需要三个元素:给哪个对象【1】的哪个属性【2】设置一个什么值【3】
        //4.1 需要先指定给哪个对象的这个age属性设置值
        Object obj = clazz.newInstance();
        //4.2 在给属性设置值之前,需要设置权限私有可见,否则报错!
        f.setAccessible(true);
        //4.3通过刚刚获取到的age属性对象,给obj对象设置值
        f.set(obj,17);
        //4.4打印查看刚刚的属性值是否设置成功
        System.out.println(f.get(obj));
    }
    /*3.单元测试2:暴力反射获取和设置私有方法*/
    @Test
    public void getFunction() throws Exception {
    
    
        //1.获取Class字节码对象
        Class<?> clazz = Person.class;
        //2.通过暴力反射获取私有方法
        /*getDeclaredMethod(m,x,y,z...)
        * m:要获取的方法名
        * x,y,z...可变参数,是这个方法的参数类型,但注意要加“.class”
        * */
        Method method = clazz.getDeclaredMethod("save",int.class,String.class);
        //3.1没有对象就通过反射的方式创建对象
        Object obj = clazz.newInstance();
        //3.2 想要执行私有方法,也需要先设置私有可见
        method.setAccessible(true);
        /*invoke(o,x,y,z...),表示通过反射技术执行方法
        * o :要执行的是哪个对象的方法
        * x,y,z...:执行这个方法【method对象代表的之前获取到的save()】时需要传入的参数
        * */
        //3.3 通过反射技术invoke(),执行目标对象obj的目标方法method【save()】
        //save()被调用时传入的参数是100,"海绵宝宝"
        method.invoke(obj,100,"海绵宝宝");
    }
}

Congratulations, you have learned a new knowledge. There are many reflection APIs, so you need to practice more

Guess you like

Origin blog.csdn.net/weixin_58276266/article/details/131478534