This article describes in detail the concept of reflection in Java, the principles, and reflection operation case demonstration.

1 the reflection

JAVA reflection mechanism is in the program running state, for any class, can know all the properties and methods of this class; You can call any method or property of any object. This ability to dynamically retrieve information and dynamically invoke methods on objects is called the Reflection mechanism of the Java language.

To dissect a class, you must first get the bytecode file object for that class. Dissection uses methods in Class. So you need to get an object of type Class for each bytecode file.

Reflection is mainly used in some common code. For example, frameworks: easy for programmers to develop, the framework is all done through reflection.

1.1 Reflection Principle

  1. The written files are.java source files and are saved on disk.
  2. Ava source files are compiled by the compiler to produce a bytecode file. Check the source file for syntax errors, which do not generate bytecode files.
  3. The JVM loads the bytecode file into memory.
  4. Java is object oriented. Everything is an object. Think of the bytecode file as an object whose type is Class.
  5. Once you have the Class type, you can use reflection to manipulate all the contents of the file:

Java regards the member variables, constructors and member methods of a Class as objects and encapsulates them into the corresponding Class. By calling the corresponding method of the Class object, the corresponding Class member variable object, constructor object and member method object can be obtained. Then call methods from those objects, as if the class were manipulating its own properties, constructors, member methods! That’s a reflex!

Supplement:

  1. Each Class has a Class object that contains information about the Class. When a new class is compiled, a.class file with the same name is generated, and the contents of the file hold the class object.
  2. Class loading corresponds to the loading of Class objects, which are dynamically loaded into the JVM when the Class is first used. Class loading can also be controlled using the class.forname (” com.mysql.jdbc.driver “) method, which returns a Class object.
  3. Reflection can provide run-time class information, and the class can be loaded at run-time, even if the.class of the class does not exist at compile time.

1.1.1 Reflection related classes

Class: Class represents Class; Java. Lang package

Member variables: fields — encapsulated in the Field object. You can use the get() and set() methods to read and modify the fields associated with the Field object;

Constructors: with and without arguments – encapsulated into the Constructor object. New objects can be created using Constructor’s newInstance().

Member methods: Normal methods, static methods, parameter methods — encapsulated into a Method object, you can use the invoke() Method to call the Method associated with the Method object;

The last three are all in the java.lang.Reflect package, while the Class is in the Java.lang package

1.1.2 Class Class

While Java programs are running, the Java runtime system always identifies all objects with what is called a runtime type, known as RTTI.

This information records the class to which each object belongs. The virtual machine usually uses the runtime type information to select the correct way to execute. The Class used to store the type information is Class. The Class Class encapsulates the runtime state of an object and interface. Objects of type Class are automatically created when the Class is loaded.

To put it bluntly:

  1. The Class Class is also a Class, but the name is highly similar to the Class keyword. Java is a case-sensitive language. But it doesn’t matter. Class is just a keyword that describes a class. Classes, on the other hand, are the classes that hold runtime information.
  2. The object content of Class Class is the type information of the Class you create. For example, if you create a shapes Class, Java generates an object whose content is the Class Class at its shapes.
  3. Objects of Class Class cannot be created as new Shapes () like ordinary classes. Its objects can only be created by the JVM because the Class has no public constructor

What can it do? The Class Class helps us analyze the Class while the program is running, which basically means getting the values in the Class. Maybe it’s a reflex. Yeah! Class is usually used in conjunction with reflection, because we provide a Class or the Class name of a Class, and the Class can give us a lot of information, such as properties/methods/modifiers/constructors/Class names, etc. And then we can reflect it further.

1.1.2.1 Obtaining a Class object

There are three basic ways to get a Class Class:

  1. Class name.class, support for basic types as well

Class c = int. Class; Class c = int[].class; Class c = String.class

  1. Object.getClass() method

Class c = obj.getClass();

  1. Class.forname () loads the Class by its full path name. This method gets the Class as long as it has the Class name;

Class c = Class. Class.forname (” cn. Itcast. Demo “);

1.1.2.1 Class Class related methods

String getName(a) // Get the class name, including the package name;
String getSimpleName(a) // Get the class name, not the package name;
Class getSupperClass(a) New Integer(100).getClass().getSupperClass() returns Class
       
        ! But new Object().getSupperClass() returns null because Object has no parent class;
       
T newInstance(a) // Create an object of this class using its parameterless constructor;
boolean isArray(a) // Whether it is an array type;
boolean isAnnotation(a) // Whether it is an annotation type;
boolean isAnnotationPresent(Class annotationClass) // Whether the current class is annotated by annotationClass;
boolean isEnum(a) // Whether it is an enumeration type;
boolean isInterface(a) // Whether it is an interface type;
boolean isPrimitive(a) // Whether it is a basic type;
boolean isSynthetic(a) // Whether it is a reference type;
Copy the code

1.2 Reflection Operations

Operation of class

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

    @Override
    public String toString(a) {
        return "Student [id=" + id + ", name=" + name + "]";
    }

    public int getId(a) {
        return id;
    }

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

    public String getName(a) {
        return name;
    }

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

    public Student(a) {
        super(a); }public Student(Integer id, String name) {
        super(a);this.id = id;
        this.name = name;
    }

    public static void eat(a) {
        System.out.println("student can eat"); }}Copy the code

1.2.1 Operate on class constructors by reflection

1.2.1.1 Through the newInstance method of Class

1.2.1.1.1 Obtaining the bytecode file object: Class

There is only one Class file for the same Class, so its Class object is unique. Three ways to obtain:

Object. GetClass (); The name of the class. The class; Class.forname (” the full path name of the Class “);

1.2.1.1.2 Calling the newInstance method on the class object

Public T newInstance() gets a newInstance object constructed using a public no-argument constructor. If no public no-argument constructor is provided, it throws an exception!

// 1. Obtain the class bytecode object
/ / way
Class c1 = Student.class;
2 / / way
Class<? extends Student> c2 = new Student().getClass();
3 / / wayClass<? > c3 = Class.forName("com.javase.reflect.Student");
// 2. Create a method using the newInstance operation with the no-parameter constructor
Student stu = (Student) c1.newInstance();
stu.setId(11);
stu.setName("lisi");
//Student [id=11, name=lisi]
System.out.println(stu);
Copy the code

1.2.1.2 Using the Constructor newInstance method

1.2.1.2.1 Get a Class instance

Object. GetClass (); The name of the class. The class; Class.forname (” the full path name of the Class “);

1.2.1.2.2 Obtain the Constructor object corresponding to the Constructor
1.2.1.2.2.1 Get the Constructor object represented by the public Constructor

public Constructor getConstructor(Class<? >… parameterTypes)

Return a Constructor object that reflects the specified public Constructor of the Class represented by this Class object.

The parameterTypes parameter is a mutable array of Class objects that identify the constructor parameterTypes in the constructor declaration order. The parameters must be of Class type in the same order as the constructor parameter parameters. A parameter of a primitive type. Class also works. You cannot manually convert to a wrapper class!

If the underlying constructor requires a form parameter of 0, the supplied array may have a length of 0 or pass null, or pass no arguments.

public Constructor<? >[] getConstructors()

Return an array containing some Constructor objects that reflect all the public constructors of the Class represented by this Class object. Returns an array of length zero if the class has no public constructor, or if the class is an array class, or if the class reflects a primitive type or void.

1.2.1.2.2.2 Gets the Constructor object represented by all constructors

That is, a Constructor object that can get all constructors, including private. This means that objects can be created even if the class constructor is private, whereas the newInstance method of the Calss class can only operate on public, parameterless constructors!

public Constructor<? >[] getDeclaredConstructors()

Return an array of Constructor objects that reflect all constructors declared for the Class represented by this Class object. These are public, protected, default (package) access, and private constructors. The elements of the return array are not sorted, nor in any particular order. If the class has a default constructor, it is included in the returned array. If the Class object represents an interface, a primitive type, an array Class, or void, then this method returns an array of length zero.

public Constructor getDeclaredConstructor(Class<? >… parameterTypes)

Return a Constructor object that reflects the specified Constructor of the Class or interface represented by this Class object. The parameterTypes parameter is an array of Class objects that identifies the constructor’s parameterTypes in declaration order. If this Class object represents an inner Class declared in a non-static context, the parameter type is included as the first parameter to display the enclosing instance.

1.2.1.2.3 Calling the Constructor object’s newInstance method

Note: For private constructor instantiations, before calling the newInstance method, you also need to set:

con.setAccessible(true); // Force the private constructor to open

Sets the accessible flag of this object to the indicated Boolean value. A value of true indicates that the reflected object should be used without Java language access checks. A value of false indicates that the reflected object should be subjected to Java language access checks.

public T newInstance(Object… initargs)

Use the Constructor represented by this Constructor object to create a new instance of the Constructor’s declared class and initialize the instance with the specified initialization parameters. Individual parameters are automatically unpacked to match the base parameters, and both the base and reference parameters are transformed by method calls if necessary.

Initargs – a variable argument. An array of objects to pass as variables to the constructor call; The value of the basic type is wrapped in a wrapper object of the appropriate type (such as Float in Float). The types of the arguments must be the same as when you obtained Constructor.

If the underlying constructor requires a form parameter of 0, the supplied array may have a length of 0 or null. Or pass no arguments.

Returns the new object created by calling the constructor represented by this object.

/* 1. Obtain the class bytecode object */
/ / way
Class c1 = Student.class;
2 / / way
Class<? extends Student> c2 = new Student().getClass();
3 / / wayClass<? > c3 = Class.forName("com.javase.reflect.Student");


/*2. Call a method to get the Constructor object */
Constructor cs = c1.getConstructor(Integer.class, String.class);


/*3. Call the instance method to create the object, passing the argument */
Student stu = (Student) cs.newInstance(123."Zhang");
//Student [id=123, name=张三]
System.out.println(stu);
Copy the code
1.2.1.2.4 Common Exceptions

Call the private constructor: NoSucnMethodException using getConstructor. The reason is that getConstructor () can only find and retrieve public constructors

Calling a private constructor using getDeclaredConstructor, but not setAccessible(true), throws: IllegalAccessException: an IllegalAccessException. The reason for this is that the constructor is private and would normally not be accessible, so brute force cracking is the only option!

1.2.1.2.5 Common Construcor methods
String getName(a) // Get constructor name;
Class getDeclaringClass(a) // Get the constructor type;
Class[] getParameterTypes(a) // Get the class type of all arguments to the constructor;
Class[] getExceptionTypes(a) // Get all exception types declared on the constructor;
T newInstance(the Object... initargs) // Call the constructor through the constructor reflection object.
setAccessible(true)  // Make the private constructor open
<T extends Annotation> T getAnnotation(Class<T> annotationClass) // Return annotations of the specified type for the element if they exist, null otherwise.
 Annotation[] getDeclaredAnnotations(a) // Return all annotations that exist directly on this element.
Copy the code

1.2.2 Operate on member variables in a class by reflection

1.2.2.1 Getting a Class instance

Object. GetClass (); The name of the class. The class; Class.forname (” the full path name of the Class “);

1.2.2.2 Obtain the Filed object of the corresponding attribute

public Field getDeclaredField(String name)

Returns a Field object that reflects the specified declared fields of the Class or interface represented by this Class object. The name argument is a String, which is the name of the field. Note that this method does not reflect the length field of the array class.

public Field[] getDeclaredFields()

Returns an array of Field objects that represent all the fields declared by the Class or interface represented by this Class object. Includes public, protected, default (package) access, and private fields, but does not include inherited fields. The elements of the return array are not sorted, nor in any particular order. If the Class or interface does not declare any fields, or if the Class object represents a primitive type, an array Class, or void, this method returns an array of length zero.

public Field getField(String name)

Returns a Field object that reflects the specified public member fields of the Class or interface represented by this Class object. The name argument is a String that specifies the short name of the desired field.

public Field[] getFields()

Returns an array of Field objects that reflect all accessible public fields of the Class or interface represented by this Class object.

The elements of the return array are not sorted, nor in any particular order. If the class or interface has no public fields accessible, or represents an array class, a primitive type, or void, then this method returns an array of length zero.

1.2.2.3 Assign values to properties by calling the set method of Field

Note: For a private property call, you need to set the operation rights via the field object’s method:

public void setAccessible(boolean flag)

Sets the accessible flag of this object to the indicated Boolean value. A value of true indicates that the reflected object should be used without Java language access checks. A value of false indicates that the reflected object should be subjected to Java language access checks.

1.2.2.3.1 Setting the Attribute value

Public void set(Object obj,Object value) obtain an instance Object of a class by using the appropriate method: obj sets the property with the specific value: value by the set method of the Field Object

/* 1. Obtain the class bytecode object */
Class c1 = Student.class;

GetFeild */ 2. GetFeild */
// Use this method to access a private property will throw :NoSuchFieldException
// Field field = c1.getField("name");
// Get an array of all public properties
// Field[] fields = c1.getFields();
// Both private and public properties can be obtained
Field field = c1.getDeclaredField("name"); 
// Both private and public properties can be obtained
Field field2 = c1.getDeclaredField("id"); 
// Get an array of all properties
// Field[] declaredFields = c1.getDeclaredFields();

// You also need to set the access permission to access the private modified properties, otherwise throw :IllegalAccessException
field.setAccessible(true);
field2.setAccessible(true);

/*3. Get an object instance */
Student stu = (Student) c1.newInstance();

Obj - instance object value- attribute value */
field.set(stu, "Ha ha");
field2.set(stu, 222);
//Student [id=222, name= ha]
System.out.println(stu);  
Copy the code
1.2.2.3.2 Common methods of the Field class
String getName(a) // Get the name of the member variable;
Class getDeclaringClass(a) // Obtain the class type of the member variable;
Class getType(a) // Get the class of the current member variable;
Object get(Object obj) // Get the value of the member variable of the obj object;
void set(Object obj, Object value) // Set the value of the obj member variable to value;
Copy the code

1.2.3 Operate on member methods of a class by reflection

1.2.3.1 Getting a Class instance

Object. GetClass (); The name of the class. The class; Class.forname (” the full path name of the Class “);

1.2.3.2 Obtain the Method object corresponding to the Method

Method getMethod(String name, Class<? >… parameterTypes)

Returns a Method object that reflects the specified public member methods of the Class or interface represented by this Class object. Get this public method

Method[] getMethods()

Returns an array of Method objects that reflect the public member methods of the Class or interface represented by this Class object, including those classes or interfaces declared by that Class or interface and that inherit from superclasses and superinterfaces. Public methods for this class and its parent class

public Method getDeclaredMethod(String name,Class<? >… parameterTypes)

Name – Method name parameterTypes – Array of Class type parameters, or a comma-separated Class type, which can be null if no parameter is provided.

Returns a Method object that reflects the specified declared Method of the Class or interface represented by this Class object. The name parameter is a String that is the name of the method, and the parameterTypes parameter is an array of Class objects that identify the parameterTypes of the method in the order they are declared.

If multiple methods with the same argument type are declared in a class, and one of them has a special return type than the others, then that method is returned. Otherwise, one of them will be chosen.

If the name is “” or” “, a NoSuchMethodException is thrown.

public Method[] getDeclaredMethods()

Returns an array of Method objects that reflect all methods declared by the Class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but not inherited methods.

1.2.3.3 Calling the Invoke Method of Method

Note: For private method calls, you must also set the operation rights on the method object:

public void setAccessible(boolean flag)

Sets the accessible flag of this object to the indicated Boolean value. A value of true indicates that the reflected object should be used without Java language access checks. A value of false indicates that the reflected object should be subjected to Java language access checks.

public Object invoke(Object obj,Object… args)

Obj — instance object… Args — the argument used for the method call

Calls the underlying Method represented by this Method object on the specified object with the specified arguments. Individual parameters are automatically unpacked to match the base parameters, and both the base and reference parameters are subject to method call conversions as needed.

If the underlying method is static, the specified OBj parameter can be ignored. This parameter can be null.

If the form parameter required by the underlying method is 0, the supplied args number length can be 0 or null, or even not.

If the underlying method is static and the class that declared the method has not yet been initialized, it is initialized.

If the method completes normally, the value returned by the method is returned to the caller; If the value is of a primitive type, it is first appropriately wrapped in an object. However, if the value’s type is a set of basic types, the array elements are not wrapped in an object; In other words, an array of primitive types is returned. If the underlying method returns type void, then the call returns NULL and the print value is automatically printed to the console after the method call completes.

1.2.3.3.1 Operation No Return value Method
/* 1. Obtain the class bytecode object */
Class c1 = Student.class;

/*2. Obtain method instance name- Class
      ... * /
// parameterTypes- specifies the parameterTypes of the method, separated by commas, or an array of parameterTypes
Method declaredMethod = c1.getDeclaredMethod("setId", Integer.class);

/*3. Get an instance object
Student stu = (Student) c1.newInstance();

/*4.invoke*/
// On the passed object, call the underlying method represented by this method object. . Args - The actual parameters used for the method call
// If the underlying method is static, then the specified obj argument can be ignored. This parameter can be null.
// If the underlying method requires a form parameter of 0, then the supplied args array can be 0 or null in length.
declaredMethod.invoke(stu, 12);
//Student [id=12, name=null]null
System.out.println(stu);
Copy the code
1.2.3.3.2 Operation has a method that returns a value
Class c1 = Student.class;
Student stu = (Student)c1.newInstance();
Method declaredMethod2 = c1.getDeclaredMethod("setId", Integer.class);
declaredMethod2.invoke(stu, 12);
Method declaredMethod = c1.getDeclaredMethod("getId");
Integer invoke = (Integer)declaredMethod.invoke(stu);
System.out.println(invoke);    / / 12
Copy the code
1.2.3.3.3 Performing static Operations
Class c1 = Student.class;
Student stu = (Student) c1.newInstance();
Method declaredMethod = c1.getDeclaredMethod("eat");
declaredMethod.invoke(null);
Copy the code

1.2.3.4 Method Common methods

String getName(a) // Get the method name;
Class getDeclaringClass(a) // Get the full pathname of the class to which the method belongs;
Class[] getParameterTypes(a) // Get the types of all parameters of the method;
Class[] getExceptionTypes(a) // Get all exception types declared on the method;
Class getReturnType(a) // Get the return value type of the method;
Object invode(Object obj, Object... args) // Call a method from a method reflection object. If the current method is an instance method, then the current object is obj. If the current method is static, then null is passed to obj. Args is the parameter of the method;

<T extends Annotation> T  getAnnotation(Class<T> annotationClass)   // Return comments of the specified type for the element if they exist, null otherwise.
 Annotation[] getDeclaredAnnotations(a)   // Return all comments that exist directly on this element.
Copy the code

1.2.4 Annotation by Reflection Operation

Get annotation on class, method, field:

Annotation[] annotations = class.getAnnotations(); Annotation[] annotations = method.getAnnotations(); Annotation[] declaredAnnotations = field.getDeclaredAnnotations();

Getting a single annotation

Annotation annotation = aClass.getAnnotation(Annotation.Class);

1.2.5 AccessibleObject

The AccessibleObject class is the parent class of Constructor, Method, and Field. It provides the ability to mark reflected objects as unchecking the default Java language access control checks when used.

The most important methods of AccessibleObject are:

Boolean isAccessible() : determines whether the current member isAccessible; Void setAccessible(Boolean flag) : Sets whether the current member is accessible. When Constructor, Method, and Field are private, we must first call the setAccessible(True) Method of the reflected object before we can perform the operation.

1.2.6 practice

Add a string to an ArrayList collection of generic type Integer!

public class ArrayListDemo {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> in = new ArrayList<Integer>();
        /* Get the Class object of the ArrayList */
        Class<? extends ArrayList> aClass = in.getClass();
        /* Reflection gets the add method object */
        Method add = aClass.getDeclaredMethod("add", Object.class);
        Reflection calls the add method */
        Object hello = add.invoke(in, "hello");
        / / return true
        System.out.println(hello);
        //[hello], add succeededSystem.out.println(in); }}Copy the code

As we know from this example, reflection can do some amazing things by bypassing generics checks.

In fact, generics are only used by the compiler to check for consistency at compile time, and the underlying source does not know that it needs to restrict the addition of only Integer types.

Since the underlying source code for the add method is add (E E), we know that E represents the object type, and generics cannot change the source code.

We use reflection to bypass the compiler’s generics check and call the source code directly. The underlying source code supports adding Object, so we can add String! But such operation suggests use with caution!