Reflection in Java (obtaining class structure, invoke method, and obtaining annotations through reflection)

1. Create an object of the runtime class

Creating objects of runtime classes is where the reflection mechanism is used the most. There are two ways to create an object of a runtime class:

Method 1: Directly call the newInstance() method of the Class object

Requirements:
1) The class must have a parameterless constructor.
2) The access rights of the constructor of the class need to be sufficient.

Method 1 steps :

1) Get the Class object of this type
2) Call newInstance()the method of the Class object to create the object

Method 2: Instantiate by obtaining the constructor object

Method 2 steps :

getDeclaredConstructor(Class ... parameterTypes)1) Obtain the constructor of the specified formal parameter type of this class through the Class class
2) Pass an object array into the formal parameter of the constructor, which contains each parameter required in the constructor.
3) By Constructorinstantiating the object.

If the scope modified by the permission modifier of the constructor is not visible, you can callsetAccessible(true)

Sample code:

import org.junit.Test;

import java.lang.reflect.Constructor;

public class TestCreateObject {
    
    
    @Test
    public void test1() throws Exception{
    
    
//        Person obj = new Person();//编译期间无法创建

        Class<?> clazz = Class.forName("com.example.ext.demo.Person");
        //clazz代表com.example.ext.demo.Person类型
        //clazz.newInstance()创建的就是Person的对象
        Object obj = clazz.newInstance();
        System.out.println(obj);
    }

    @Test
    public void test2()throws Exception{
    
    
        Class<?> clazz = Class.forName("com.example.ext.demo.Person");
        //java.lang.InstantiationException: com.example.ext.demo.Person
        //Caused by: java.lang.NoSuchMethodException: com.example.ext.demo.Person.<init>()
        //即说明Person没有无参构造,就没有无参实例初始化方法<init>
        Object stu = clazz.newInstance();
        System.out.println(stu);
    }

    @Test
    public void test3()throws Exception{
    
    
        //(1)获取Class对象
        Class<?> clazz = Class.forName("com.example.ext.demo.Person");
        /*
         * 获取Person类型中的有参构造
         * 如果构造器有多个,我们通常是根据形参【类型】列表来获取指定的一个构造器的
         * 例如:public Person(String title, int num)
         */
        //(2)获取构造器对象
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class);

        //(3)创建实例对象
        // T newInstance(Object... initargs)  这个Object...是在创建对象时,给有参构造的实参列表
        Object obj = constructor.newInstance("张三", 18);
        System.out.println(obj);
    }
}

2. Get the full structure of the runtime class

Can get: package, modifier, type name, parent class (including generic parent class), parent interface (including generic parent interface), member (property, constructor, method), annotation (on class, on method) , attributes).

2.1 Related APIs

//1.实现的全部接口
public Class<?>[] getInterfaces()   
//确定此对象所表示的类或接口实现的接口。 

//2.所继承的父类
public Class<? Super T> getSuperclass()
//返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

//3.全部的构造器
public Constructor<T>[] getConstructors()
//返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors()
//返回此 Class 对象表示的类声明的所有构造方法。

//Constructor类中:
//取得修饰符: 
public int getModifiers();
//取得方法名称: 
public String getName();
//取得参数的类型:
public Class<?>[] getParameterTypes();

//4.全部的方法
public Method[] getDeclaredMethods()
//返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods()  
//返回此Class对象所表示的类或接口的public的方法

//Method类中:
public Class<?> getReturnType()
//取得全部的返回值
public Class<?>[] getParameterTypes()
//取得全部的参数
public int getModifiers()
//取得修饰符
public Class<?>[] getExceptionTypes()
//取得异常信息

//5.全部的Field
public Field[] getFields() 
//返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields() 
//返回此Class对象所表示的类或接口的全部Field。

//Field方法中:
public int getModifiers()
//以整数形式返回此Field的修饰符
public Class<?> getType()  
//得到Field的属性类型
public String getName()  
//返回Field的名称。

//6. Annotation相关
get Annotation(Class<T> annotationClass) 
getDeclaredAnnotations() 

//7.泛型相关
//获取父类泛型类型:
Type getGenericSuperclass()
//泛型类型:ParameterizedType
//获取实际的泛型类型参数数组:
getActualTypeArguments()

//8.类所在的包
Package getPackage() 

2.2 Get all properties and related details


import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.junit.Test;

import com.example.java1.Person;

public class FieldTest {
    
    
	
    @Test
    public void test1(){
    
    
        
        Class clazz = Person.class;
        //getFields():获取到运行时类本身及其所有的父类中声明为public权限的属性
    //		Field[] fields = clazz.getFields();
    //
    //		for(Field f : fields){
    
    
    //			System.out.println(f);
    //		}
        
        //getDeclaredFields():获取当前运行时类中声明的所有属性,包括 private的属性
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
    
    
            System.out.println(f);
        }
    }
	
	//权限修饰符  变量类型  变量名
    @Test
    public void test2(){
    
    
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
    
    
            //1.权限修饰符
            /*
            * 0x是十六进制
            * PUBLIC           = 0x00000001;  1    1
            * PRIVATE          = 0x00000002;  2	10
            * PROTECTED        = 0x00000004;  4	100
            * STATIC           = 0x00000008;  8	1000
            * FINAL            = 0x00000010;  16	10000
            * ...
            *
            * 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0
            *
            * mod = 17          0x00000011
            * if ((mod & PUBLIC) != 0)  说明修饰符中有public
            * if ((mod & FINAL) != 0)   说明修饰符中有final
            */
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + "\t");

//            //2.数据类型
            Class type = f.getType();
            System.out.print(type.getName() + "\t");
//
//            //3.变量名
            String fName = f.getName();
            System.out.print(fName);
//
            System.out.println();
        }
    }
}

2.3 Get all methods and related details


import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.junit.Test;

import com.example.java1.Person;

public class MethodTest {
    
    

    @Test
    public void test1() {
    
    

        Class clazz = Person.class;
        // getMethods():获取到运行时类本身及其所有的父类中声明为public权限的方法
        // Method[] methods = clazz.getMethods();
        //
        // for(Method m : methods){
    
    
        // System.out.println(m);
        // }

        // getDeclaredMethods():获取当前运行时类中声明的所有方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
    
    
            System.out.println(m);
        }
    }

    // 注解信息
    // 权限修饰符 返回值类型 方法名(形参类型1 参数1,形参类型2 参数2,...) throws 异常类型1,...{}
    @Test
    public void test2() {
    
    
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
    
    
            // 1.获取方法声明的注解
            Annotation[] annos = m.getAnnotations();
            for (Annotation a : annos) {
    
    
                System.out.println(a);
            }

            // 2.权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

            // 3.返回值类型
            System.out.print(m.getReturnType().getName() + "\t");

            // 4.方法名
            System.out.print(m.getName());
            System.out.print("(");
            // 5.形参列表
            Class[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null && parameterTypes.length == 0)) {
    
    
                for (int i = 0; i < parameterTypes.length; i++) {
    
    

                    if (i == parameterTypes.length - 1) {
    
    
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }

                    System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
                }
            }

            System.out.print(")");

            // 6.抛出的异常
            Class[] exceptionTypes = m.getExceptionTypes();
            if (exceptionTypes.length > 0) {
    
    
                System.out.print("throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
    
    
                    if (i == exceptionTypes.length - 1) {
    
    
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }
            System.out.println();
        }
    }
}

2.4 Get other structures (constructor, parent class, interface, package, annotation, etc.)

package com.example.java2;

import com.example.java1.Person;
import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class OtherTest {
    
    

    /*
    	获取当前类中的所有的构造器(包括私有构造器)
     */
    @Test
    public void test1(){
    
    
        Class clazz = Person.class;
        Constructor[] cons = clazz.getDeclaredConstructors();
        for(Constructor c :cons){
    
    
            System.out.println(c);
        }
    }
    /*
    	获取运行时类的父类
     */
    @Test
    public void test2(){
    
    
        Class clazz = Person.class;
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);//class com.example.java1.Creature
    }
    /*
    	获取运行时类的所在的包
     */
    @Test
    public void test3(){
    
    
        Class clazz = Person.class;
        Package pack = clazz.getPackage();
        System.out.println(pack);

    }
    /*
    	获取运行时类的注解
     */
    @Test
    public void test4(){
    
    
        Class clazz = Person.class;
        Annotation[] annos = clazz.getAnnotations();
        for (Annotation anno : annos) {
    
    
            System.out.println(anno);
        }
    }

    /*
    	获取运行时类所实现的接口
     */
    @Test
    public void test5(){
    
    
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for (Class anInterface : interfaces) {
    
    
            System.out.println(anInterface);
        }
    }
    /*
    	获取运行时类的带泛型的父类
     */
    @Test
    public void test6(){
    
    
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);//com.example.java1.Creature<java.lang.String>
    }
}

2.5 Obtain generic parent class information

Sample code to obtain generic parent class information:

/* Type:
 * (1)Class
 * (2)ParameterizedType   
 * 		例如:Father<String,Integer>
 * 			ArrayList<String>
 * (3)TypeVariable
 * 		例如:T,U,E,K,V
 * (4)WildcardType
 * 		例如:
 * 		ArrayList<?>
 * 		ArrayList<? super 下限>
 * 		ArrayList<? extends 上限>
 * (5)GenericArrayType
 * 		例如:T[]
 * 	
 */
public class TestGeneric {
    
    
    public static void main(String[] args) {
    
    
        //需求:在运行时,获取Son类型的泛型父类的泛型实参<String,Integer>
        
        //(1)还是先获取Class对象
        Class clazz = Son.class;//四种形式任意一种都可以
        
        //(2)获取泛型父类
        //  Class sc = clazz.getSuperclass();
        //  System.out.println(sc);
        /*
        * getSuperclass()只能得到父类名,无法得到父类的泛型实参列表
        */
        Type type = clazz.getGenericSuperclass();
        
        // Father<String,Integer>属于ParameterizedType
        ParameterizedType pt = (ParameterizedType) type;
        
        //(3)获取泛型父类的泛型实参列表
        Type[] typeArray = pt.getActualTypeArguments();
        for (Type type2 : typeArray) {
    
    
            System.out.println(type2);
        }
    }
}
//泛型形参:<T,U>
class Father<T,U>{
    
    
	
}
//泛型实参:<String,Integer>
class Son extends Father<String,Integer>{
    
    
	
}

2.6 Obtaining inner class or outer class information

public Class<?>[] getClasses(): Returns all public inner classes and inner interfaces. Includes public class and interface members inherited from the superclass and public class and interface members declared by the class.

public Class<?>[] getDeclaredClasses(): Returns an array of Class objects reflecting all the classes and interfaces declared to be members of the class represented by this Class object. Includes public, protected, default (package) access, and private classes and interfaces declared by the class, but excludes inherited classes and interfaces.

public Class<?> getDeclaringClass(): If the class or interface represented by this Class object is an internal class or internal interface, return its external class or external interface, otherwise return null.

Class<?> getEnclosingClass(): Returns the outer class of an inner class

@Test
public void test5(){
    
    
    Class<?> clazz = Map.class;
    Class<?>[] inners = clazz.getDeclaredClasses();
    for (Class<?> inner : inners) {
    
    
        System.out.println(inner);
    }
    
    Class<?> ec = Map.Entry.class;
    Class<?> outer = ec.getDeclaringClass();
    System.out.println(outer);
}

2.7 Summary

  1. Knowing so many APIs of reflection, in fact, in actual operation, the operation code for obtaining class information through reflection is not often developed, and it is frequently used in the design of the framework.

  2. It is mainly necessary to be familiar with java.lang.reflectthe function of the package and the reflection mechanism.

3. Call the specified structure of the runtime class

3.1 Call the specified attribute

In the reflection mechanism, Fieldthe properties in the class can be manipulated directly through the class, and the operation of setting and obtaining the content of the property can be completed through the methods provided Fieldby the class .set()get()

(1) Get the Class object of this type

Class clazz = Class.forName("包.类名");

(2) Get the attribute object

Field field = clazz.getDeclaredField("属性名");

(3) If the permission modifier of the attribute is not public, then the attribute needs to be set to be accessible

field.setAccessible(true);

(4) Create an instance object: If the operation is a non-static property, you need to create an instance object

Object obj = clazz.newInstance(); //有公共的无参构造

Object obj = 构造器对象.newInstance(实参...);//通过特定构造器对象创建实例对象

(4) Set the attribute content of objthis on the specified objectField

field.set(obj,"属性值");

If the static variable is operated, the instance object can be omitted and represented by null

(5) Obtain the attribute content of objthis object on the specified objectField

Object value = field.get(obj);

If the static variable is operated, the instance object can be omitted and represented by null

Sample code:

package com.example.reflect;

public class Student {
    
    
    private int id;
    private String name;

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

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

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

package com.example.reflect;

import java.lang.reflect.Field;

public class TestField {
    
    
    public static void main(String[] args)throws Exception {
    
    
        //1、获取Student的Class对象
        Class clazz = Class.forName("com.example.reflect.Student");

        //2、获取属性对象,例如:id属性
        Field idField = clazz.getDeclaredField("id");

        //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
        idField.setAccessible(true);

        //4、创建实例对象,即,创建Student对象
        Object stu = clazz.newInstance();

        //5、获取属性值
        /*
         * 以前:int 变量= 学生对象.getId()
         * 现在:Object id属性对象.get(学生对象)
         */
        Object value = idField.get(stu);
        System.out.println("id = "+ value);

        //6、设置属性值
        /*
         * 以前:学生对象.setId(值)
         * 现在:id属性对象.set(学生对象,值)
         */
        idField.set(stu, 2);

        value = idField.get(stu);
        System.out.println("id = "+ value);
    }
}

3.2 Call the specified method

insert image description here

(1) Get the Class object of this type

Class clazz = Class.forName("包.类名");

(2) Get the method object

Method method = clazz.getDeclaredMethod("方法名",方法的形参类型列表);

(3) Create an instance object

Object obj = clazz.newInstance();

(4) Call method

Object result = method.invoke(obj, 方法的实参值列表);

If the scope modified by the permission modifier of the method is not visible, you can also callsetAccessible(true)

If the method is a static method, the instance object can also be omitted (it’s okay to write), and null is used instead

Sample code:

package com.example.reflect;

import org.junit.Test;

import java.lang.reflect.Method;

public class TestMethod {
    
    
    @Test
    public void test()throws Exception {
    
    
        // 1、获取Student的Class对象
        Class<?> clazz = Class.forName("com.example.reflect.Student");

        //2、获取方法对象
        /*
         * 在一个类中,唯一定位到一个方法,需要:(1)方法名(2)形参列表,因为方法可能重载
         *
         * 例如:void setName(String name)
         */
        Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);

        //3、创建实例对象
        Object stu = clazz.newInstance();

        //4、调用方法
        /*
         * 以前:学生对象.setName(值)
         * 现在:方法对象.invoke(学生对象,值)
         */
        Object setNameMethodReturnValue = setNameMethod.invoke(stu, "张三");

        System.out.println("stu = " + stu);
        //setName方法返回值类型void,没有返回值,所以setNameMethodReturnValue为null
        System.out.println("setNameMethodReturnValue = " + setNameMethodReturnValue);

        Method getNameMethod = clazz.getDeclaredMethod("getName");
        Object getNameMethodReturnValue = getNameMethod.invoke(stu);
        //getName方法返回值类型String,有返回值,getNameMethod.invoke的返回值就是getName方法的返回值
        System.out.println("getNameMethodReturnValue = " + getNameMethodReturnValue);//张三
    }

    @Test
    public void test02()throws Exception{
    
    
        Class<?> clazz = Class.forName("com.example.ext.demo.Student");
        Method printInfoMethod = clazz.getMethod("printInfo", String.class);
        //printInfo方法是静态方法
        printInfoMethod.invoke(null,"北大");
    }
}

3.3 About the use of the setAccessible method

  • MethodBoth and Fieldobjects Constructorhave setAccessible()methods.
  • setAccessibleA switch to enable and disable access security checks.
  • The parameter value trueindicates that the reflected object should cancel Javathe language access check when it is used.
    • Improve the efficiency of reflection. If reflection must be used in the code, and the code needs to be called frequently, please set it to true.
    • Makes private members that are otherwise inaccessible accessible
  • The parameter value falseindicates that the reflected object should implement Java language access checks.

4. Read annotation information

A complete annotation should contain three parts:
(1): declaration
(2): use
(3): read

4.1 Declare custom annotations

package com.example.annotation;

import java.lang.annotation.*;

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    
    
    String value();
}
package com.example.annotation;

import java.lang.annotation.*;

@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    
    
    String columnName();
    String columnType();
}
  • Custom annotations can use four meta-annotations @Retention, @Target, @Inherited, @Documented, to indicate its life cycle, usage location, whether it is inherited, and whether it is generated into the API document.
  • AnnotationMembers of are declared in Annotationthe definition as an abstract method with no parameters and a return value, which we also call configuration parameters . The return value type can only be an array of eight basic data types, String type , Class type , enum type , Annotation type , and all the above types
  • You can specify a default return value for an abstract method using defaultthe keyword
  • If the defined annotation contains an abstract method, the return value must be specified when used, unless it has a default value. The format is " method name = return value ". If there is only one abstract method that needs to be assigned a value, and the method name is "value=" value, you can omit "value=". Therefore, if the annotation has only one abstract method member, it is recommended to use the method name value.

4.2 Using custom annotations

package com.example.annotation;

@Table("t_stu")
public class Student {
    
    
    @Column(columnName = "sid",columnType = "int")
    private int id;
    @Column(columnName = "sname",columnType = "varchar(20)")
    private String name;

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

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

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

4.3 Reading and processing custom annotations

Custom annotations must be accompanied by the information processing flow of annotations to be meaningful.

Annotations defined by ourselves can only be read using reflected code. So the declaration cycle of the custom annotation must be RetentionPolicy.RUNTIME.

package com.example.annotation;

import java.lang.reflect.Field;

public class TestAnnotation {
    
    
    public static void main(String[] args) {
    
    
        Class studentClass = Student.class;
        Table tableAnnotation = (Table) studentClass.getAnnotation(Table.class);
        String tableName = "";
        if(tableAnnotation != null){
    
    
            tableName = tableAnnotation.value();
        }

        Field[] declaredFields = studentClass.getDeclaredFields();
        String[] columns = new String[declaredFields.length];
        int index = 0;
        for (Field declaredField : declaredFields) {
    
    
            Column column = declaredField.getAnnotation(Column.class);
            if(column!= null) {
    
    
                columns[index++] = column.columnName();
            }
        }
        
        String sql = "select ";
        for (int i=0; i<index; i++) {
    
    
            sql += columns[i];
            if(i<index-1){
    
    
                sql += ",";
            }
        }
        sql += " from " + tableName;
        System.out.println("sql = " + sql);
    }
}

5. Experience the dynamics of reflection

Experience 1:

public class ReflectionTest {
    
    

    //体会反射的动态性:动态的创建给定字符串对应的类的对象
    public <T> T getInstance(String className) throws Exception {
    
    

        Class clazz = Class.forName(className);

        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        return (T) constructor.newInstance();

    }

    @Test
    public void test1() throws Exception {
    
    
        String className = "com.example.java1.Person";
        Person p1 = getInstance(className);
        System.out.println(p1);
    }
}

Experience 2:

public class ReflectionTest {
    
    
    //体会反射的动态性:动态的创建指定字符串对应类的对象,并调用指定的方法
    public Object  invoke(String className,String methodName) throws Exception {
    
    
        Class clazz = Class.forName(className);
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        //动态的创建指定字符串对应类的对象
        Object obj = constructor.newInstance();

        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true);
        return method.invoke(obj);
    }

    @Test
    public void test2() throws Exception {
    
    
        String info = (String) invoke("com.example.java1.Person", "show");

        System.out.println("返回值为:" + info);

    }
}

Experience 3:

public class ReflectionTest {
    
    
	@Test
    public void test1() throws Exception {
    
    
        //1.加载配置文件,并获取指定的fruitName值
        Properties pros = new Properties();
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties");
        pros.load(is);
        String fruitStr = pros.getProperty("fruitName");
        //2.创建指定全类名对应类的实例
        Class clazz = Class.forName(fruitStr);
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Fruit fruit = (Fruit) constructor.newInstance();
        //3. 调用相关方法,进行测试
        Juicer juicer = new Juicer();
        juicer.run(fruit);

    }

}

interface Fruit {
    
    
	public void squeeze();
}

class Apple implements Fruit {
    
    
	public void squeeze() {
    
    
		System.out.println("榨出一杯苹果汁儿");
	}
}

class Orange implements Fruit {
    
    
	public void squeeze() {
    
    
		System.out.println("榨出一杯桔子汁儿");
	}
}

class Juicer {
    
    
	public void run(Fruit f) {
    
    
		f.squeeze();
	}
}

Among them, the configuration file [config.properties] is stored under the src of the current Module

Guess you like

Origin blog.csdn.net/weixin_43847283/article/details/130549345