Java reflection (easy to understand)

Table of contents

1. Introduction to reflection

2. Reflection API

2.1 Obtain the bytecode object corresponding to the class (three types)

2.2 Common methods

3. The application of reflection

3.1 Create: Test Material Class

3.2 Get class object

3.3 Get member variables

3.4 Get the member method of the class through the bytecode object

3.5 Get the constructor of the class through the bytecode object

4. Create objects


1. Introduction to reflection

        Reflection (reflection) is one of the characteristics of the Java programming language, which allows the running Java program to check itself. The resources encapsulated by private can only be accessed inside the class, not outside, but reflection can directly manipulate the private properties of the class. Reflection can get all the information of a class at runtime (including member variables, member methods, constructors, etc.), and can manipulate the fields, methods, constructors, etc. of the class.

        To dissect a class, you must first obtain the bytecode file object of the class. The dissection uses the methods in the Class class. Therefore, it is first necessary to obtain the object of the Class type corresponding to each bytecode file.

        Reflection is to map various components in the java class into individual Java objects .
        For example: A class has information such as member variables, methods, construction methods, packages, etc., and reflection technology can be used to dissect a class and map each component into each object. (Actually: these member methods, construction methods, and adding classes in a class are all described by a class)
        When loading: The origin of the Class object is to read the .class file into memory and create a Class object for it.

        class class

        Instances of the Class class represent classes and interfaces in a running Java application . That is, there are more than N instances in the jvm, and each class has the Class object. (including primitive data types)
        Class has no public constructor. Class objects are constructed automatically by the Java virtual machine when loading a class and by calling the defineClass method in the class loader. That is, we don't need to handle the creation ourselves, the JVM has already created it for us.

        We know that the Spring framework can help us create and manage objects. When we need an object, we don't need to manually new the object ourselves, we can 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.   

        Bean

        1. Java is object-oriented, and objects have methods and properties, so object instances are needed to call methods and properties (that is, instantiation);

        2. All classes with methods or attributes need to be instantiated, so that they can be visualized to use these methods and attributes;

        3. Rules: All subclasses and classes with methods or attributes must be annotated to register Bean to Spring IoC ; ( @Component , @Repository , @Controller , @Service , @Configration )

        4. Understand Bean as the agent or spokesperson of the class (in fact, it is realized through reflection and proxy), so that it can represent what the class should own

        5. In Spring, if you mark an @ symbol, then Spring will take a look and get a Bean (register) or give a Bean (use) from here

2. Reflection API

2.1 Obtain the bytecode object corresponding to the class (three types)

①Call the getClass() method of an object of a certain class, namely: object.getClass();

Person p = new Person();
Class clazz = p.getClass();

        Note: The getClass() method in the Object class is used here, because all classes inherit the Object class, so call the getClass() method in the Object class to obtain it.

②Call the class attribute class of the class to obtain the Class object corresponding to the class, namely: class name.class

Class clazz = Person.class;

Use the forName() static method in the Class class (the safest and best performance) namely: Class.forName("full path of the class")

Class clazz = Class.forName("类的全路径");

        Note: During runtime, only one Class object is generated for a class.

        The third method is commonly used in the three methods, and what is the purpose of reflection when the first object is available. The second type of package that needs to import classes has too strong dependencies, and compile errors will be thrown if the package is not imported.

2.2 Common methods

        After we obtain the Class object of the class we want to operate, we can obtain and view the methods and properties in the class through the methods in the Class class.

//获取包名、类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名

//获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)

//获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)

//获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)

//反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(222,"韦小宝");//执行有参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法

//反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

//反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

3. The application of reflection

3.1 Test material category

Create package: com.reflection
Create class: Student.java*

package com.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("不玩游戏,学Java!");
    }
    public void sunDay(int n){
        System.out.println("卷起来,没有假!");
    }
    //5.为了查看学生对象的具体属性与属性值,重写toString()
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3.2 Get class object

Since the third method is often used, the third method is used below.

Create package: com.reflection
Create class: TestReflect.java

/*本类用于反射的测试*/
public class TestReflect {
    //1.可以创建程序的入口函数main()--此处不用
    //2.通过单元测试方法,获取目标类Student对应的字节码对象
    @Test
    public void getClazz() throws ClassNotFoundException {
        //练习获取字节码对象的3种方式
        Class<?> clazz1 = Class.forName("com.review.Student");
        Class<?> clazz2 = Student.class;
        Class<?> clazz3 = new Student().getClass();

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

3.3 Get member variables

/**本类用来测试反射*/
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.获取Student类对应的字节码对象
        Class<?> clazz = Class.forName("com.review.Student");
        //2.通过Student类对应的字节码对象获取Student类中的成员变量们
        Field[] fs = clazz.getFields();
        //3.遍历数组,获取Student类中的每个成员变量的具体信息
        /*注意!目前成员变量的修饰符必须是public的才能获取到*/
        for(Field f : fs){
            System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
            System.out.println(f.getType());//通过本轮循环到的字段对象获取字段的类型
        }

    }
}

3.4 Get the member method of the class through the bytecode object

/**本类用来测试反射*/
public class TestReflect {
    //5.通过单元测试方法,获取Student类中的成员方法
    @Test
    public void getFunction() {
        //1.获取Student类对应的字节码对象
        Class<?> clazz = Class.forName("com.review.Student");
        //2.通过Student类对应的字节码对象获取Student类中的成员方法们
        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));//打印方法参数的数组
        }

    }
}

3.5 Get the constructor of the class through the bytecode object

/**本类用来测试反射*/
public class TestReflect {
    //6.通过单元测试方法,获取Student类中的构造方法
    @Test
    public void getCons() {
        //1.获取字节码对象
        Class<?> clazz = Class.forName("com.review.Student");
        //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. Create objects

/**本类用来测试反射*/
public class TestReflect {
//7.通过单元测试方法,创建Student目标类的对象
    @Test
    public void getObject() throws Exception {
        //1.获取字节码对象
        Class<?> clazz = Class.forName("com.review.Student");
        
        //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);
    }
}

Reference Link 1: Java Basics - Reflection (Very Important)_Javav

Reference link 2: Learn JAVA reflection learning together (super detailed)

Guess you like

Origin blog.csdn.net/qq_51515673/article/details/124830558