This article will take you to the bottom of reflection

preface

Human communication needs language, and the interaction between human and robot also needs language. Since the birth of computer, computer language has experienced machine language, assembly language and high-level language. Of all the programming languages, only machine language can be directly understood and executed by the computer, while all other programming languages must be translated into machine language before they can interact with the computer.

Static and dynamic languages

For us, the most exposed are high-level languages, including C, C++, Java, Python, JavaScript, etc. These high-level languages can be roughly divided into two categories, namely dynamic and static languages

  • Static language

In layman’s terms, a language can be considered static if the type of a variable is known at compile time, as we are familiar with Java, C, C++, etc., which must specify the type of each variable when writing code.

  • Dynamic languages

Dynamic languages generally refer to scripting languages, such as Python and JavaScript, which do not have to specify types when writing code.

  • Dynamic languages VS static languages

Intuitively, static languages specify variable types when code is compiled; Dynamic languages check variable types only at run time. So, for a dynamic language, we can change its structure at run time, that is, the code at run time can change its structure according to certain conditions.

According to the classification, Java is a static language, but because Java provides reflection mechanism, making Java become a quasi-dynamic language, using reflection can obtain similar characteristics of dynamic language, making programming more flexible.

Let’s take a closer look at what Java reflection is, how it is used, and why it is used.

A basic overview of Java reflection

  • What is reflection?

JAVA reflection allows you to know all the properties and methods of any entity class in the runtime state. For any object, you can call any of its methods and properties; This ability to dynamically retrieve information and invoke object methods is called the Reflection mechanism of the Java language.

In the daily development process, we often encounter member variables and methods in a class that are private. These members and methods are not developed externally, but can be dynamically retrieved at runtime through Java’s reflection mechanism. Java reflection is a program that dynamically loads a class based on its fully qualified name, creates an object, and calls any properties and methods in that object at runtime.

So, why learn reflexes?

We imagine such a scene, when we are in need of some functions in application, the way we usually use is new an object first, then we need was obtained from the object function method, but we had thought, if a program that we support plug-ins, but we don’t know what is the plug-in class, in this case? Fortunately, reflection solves this problem by reading the class name from the configuration file at runtime, and then dynamically retrieving the object type information. Therefore, the biggest benefit of reflection is that components are built dynamically at run time, making code more flexible and versatile.

In normal mode, import the required package class name, instantiate the package through new, and obtain the instantiated object

Reflection mode: ① instantiate the object ②getClass method ③ get the complete “package class” name

2. Understand the Class Class and get the Class instance

Now that we’re using reflection to create objects, how do we create classes? Does it reflect the same object for different instance objects?

There are three ways to get a Class

  • Of a given classFully qualified class name, through the static methods of the Class ClassforNameTo obtain
Class c=Class.forName("java.Lang.String")
Copy the code
  • Given a specific class that can be passedclassProperties for
Class c=Person.class;
Copy the code
  • Given some classThe instanceTo call the instancegetClass()Method to get a Class object
Class c=person.getClass();
Copy the code

Example code (take the first approach as an example)

  • Create a Person class
class People{
    private int id;
    private int age;
    privateString name; . }Copy the code
  • The test class 1
public class TestReflrction01 {
    public static void main(String[] args) throws ClassNotFoundException {
        // Get the Class object of the Class by reflection
        Class c1=Class.forName("reflection.People"); System.out.println(c1); }}Copy the code

The output of the test class is: class reflection.People

So, the question is again, for different instance objects to get the same Class? In this way, we get the hashCode of the Class that the different instance objects get, and if the hashCode is the same, they prove to be the same Class

  • The test class 2
public class TestReflrction01 {
    public static void main(String[] args) throws ClassNotFoundException {
        // Get the Class object of the Class by reflection
        Class c1=Class.forName("reflection.People");
        System.out.println(c1);
        // Only one Class object exists in memory
        // Once a Class is loaded, the entire structure of the Class is encapsulated in the Class object
        Class c2=Class.forName("reflection.People");
        Class c3=Class.forName("reflection.People");
        Class c4=Class.forName("reflection.People"); System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); System.out.println(c4.hashCode()); }}Copy the code

The output of the test class is as follows:

class reflection.People
460141958
460141958
460141958
Copy the code

Therefore, we can conclude that different instance objects of the same class reflect the same and unique class object.

3. The loading process of the class and the memory analysis when the reflection creates the object

3.1 Analysis of the loading process of classes

We learned how to create a Class, but we must have wondered why it is possible to create a Class dynamically. To understand how this works, we must first understand JVM memory.

Let’s start with a flow chart like this

This diagram describes in detail the Java file execution process we write, because it involves a lot of KNOWLEDGE of JVM, interested students can first look at my previous article a introduction to the JVM virtual machine, will continue to supplement relevant knowledge, here, we mainly analyze the class loading process.

We all know that Java programs are executed on virtual machines. The Java virtual machine loads the data describing the Class from the Class file into memory, and verifies, converts, parses, and initializes the data to form Java types that can be directly used by the virtual machine.

Validation, preparation, and parsing are collectively referred to as joins. Let’s analyze the loading process of classes in detail

  • loading
  • Gets the binary byte stream that defines a class by its fully qualified name
  • Transform the static storage structure represented by this byte stream into a method area runtime data structure
  • Generate a java.lang.Class object representing the Class in memory as an access point to the Class’s various data in the method area
  • Chaining: The process of merging the binaries of Java classes into the JVM runtime
  • Validation: To ensure that the loaded class information complies with the JVM specification and that there are no security issues
  • Preparation: The formal phase of allocating memory for class variables (static) and setting the default initial values for class variables. This memory will be allocated in the method area
  • Resolution: The process of replacing symbolic references (constant names) in the virtual machine constant pool with direct references (addresses)

Note: For symbolic reference resolution in the constant pool, it is up to you to decide whether to resolve the symbolic reference in the constant pool as soon as the class loader is loaded, or to wait until a symbolic reference is resolved before it is used.

  • Initialize the

The initialization phase is the final phase of the class loading process, where the Java virtual machine actually executes the Java program code written in the class, handing over control to the application. The initialization steps are as follows:

  • The process of executing the class constructor () method. Class constructor () methods are generated by combining assignments that automatically collect all class variables in a class at compile time with statements in static code blocks. (Class constructors build class information, not objects of that class.)
  • When initializing a class, if the parent class is not initialized, the initialization of the parent class must be triggered first
  • The virtual machine ensures that a class’s () methods are locked and synchronized correctly in a multithreaded environment.

At this point, we might wonder, when does class initialization happen? In fact, initialization only happens when a class actively references it.

  • An active reference to a class
  • When the virtual machine starts, initialize the class in which the main method resides
  • New An object of a class
  • Call static members of the class (except final constants) and static methods
  • Make a reflection call to the class using the methods of the java.lang.Reflect package
  • When initializing a class, if its parent class is not initialized, its parent class is initialized first

A passive reference to a class will not be initialized. Yes, the classes listed below will not be initialized

  • A passive reference to a class
  • When accessing a static field, only classes that actually declare the field are initialized. For example, when referring to a static variable of a parent class through a subclass, it does not cause the subclass to be initialized
  • Defining a class reference through an array does not trigger initialization of the class
  • Referring to constants does not trigger initialization of this class (constants are stored in the calling class’s constant pool during the link phase)

3.2 Memory analysis of objects created using reflection

Above we detailed analysis of Java memory distribution and class loading process, at this point, we write the code is already in the run time, we know that reflection can dynamically create objects at run time, so how does it work? In the following article, we analyze it in detail

The image above shows the memory distribution after our Class loading process, where each Class creates a Class representing its own Class in the heap. Remember, this Class Class is used to create Class objects, so let’s move on.

When we new A in the stack, it first finds the Class Class in the heap, because the Class Class is the access point to the various data in the method area Class A. The corresponding class information is then brought to the heap for instantiation.

It is easy to understand why objects can be reflected that can be dynamically retrieved at runtime. In the following articles, we’ll explain in detail how to use reflection, how to create runtime class objects using reflection, how to get the complete structure of a runtime class, and how to invoke the specified structure of a runtime class.

3.3 Overview of reflection related apis and major features provided

Reflection related API

  • Java.lang. Class: Represents a Class
  • Java.lang.reflect. Method: Represents the Method of a class
  • Java.lang.reflect. Field: Represents a class member variable
  • Java. Lang. Reflect. Constructor: on behalf of the Constructor of a class

The main function provided by the reflection mechanism

  • Determine the class to which any object belongs at run time
  • Constructs an object of any class at run time
  • Determine which member variables and methods any class has at run time
  • Get information about generics at runtime
  • Call the member variables and methods of any object at run time
  • Annotations are processed at run time
  • Generating dynamic proxies

4. Create a runtime class object

While the program is running, the Java runtime system always maintains a type identity called the runtime for all objects. This information keeps track of the class to which each object belongs. The virtual machine selects the appropriate method to execute using the runtime type information. Java provides a special Class to access this information, and the Class that holds this information is called Class. This name is very confusing, because there is a method in the Object Class that gets an instance of the Class, and this method can be inherited by all subclasses

public final Class getClass
Copy the code

4.1 Three ways to obtain a Class object

In the Java API, there are three ways to get Class objects

  • Use the class.forname static method

The prerequisite for using this method is to know the full path name of the class

// The Class name can be obtained by the static forName() method of the Class Class, which may throw ClassNotFoundException
       Class c1= Class.forName("reflection.People");
Copy the code
  • Use the.class method of the class

This method works for classes that are explicitly manipulated before compilation

  // Method 2: If it is the same as a specific class, use the class attribute to obtain the value
       Class c3=People.class;
Copy the code
  • Use the getClass() method of the class object

This method is applicable to the case where there are object instances

// Method two: call getClass() of this instance
       People people=new People();
Copy the code
  • Code validation
public class TestReflection {
    public static void main(String[] args) throws ClassNotFoundException {

        // The Class name can be obtained by the static forName() method of the Class Class, which may throw ClassNotFoundException
       Class c1= Class.forName("reflection.People");
       System.out.println(c1);

       // Method two: call getClass() of this instance
       People people=new People();
       Class c2=people.getClass();
       System.out.println(c2);

        // Method 3: If it is the same as a specific class, use the class attribute to obtain the valueClass c3=People.class; System.out.println(c3); }}Copy the code

conclusion

In fact, for each Class, the JRE reserves an unchanging object of type Class for it. A Class object contains information about a particular structure. Class is summarized as follows:

  • Class is itself a Class
  • Class objects can only be created by the system
  • A loaded Class will only have one Class instance in the JVM
  • A Class object corresponds to one loaded into the JVM. The Class file
  • Each instance of a Class remembers which Class instance it was generated from
  • Class provides a complete view of all loaded structures in a Class
  • The Class Class is the root of Reflection. For any Class that you want to dynamically load and run, you have to get the corresponding Class object first

5. Get the complete structure of the runtime class

In the previous article, we explained how to use reflection to create Class objects. What can we do once we have the actual objects? What specific ways does reflection give us to operate?

In the java.lang.reflect package, there are three classes Field, Method, and Constructor that describe the class’s properties, methods, and constructors, respectively. All three classes have a method called getName,

The getFields, getMethods, and getConstructord methods in the Class Class return the public(array of properties, methods, and constructors) provided by the Class, respectively, including the public members of the parent Class.

The getDeclaredFields, getDeclaredMethods, and getDeclaredConstructors methods on the Class Class return (all) properties, methods, and constructors declared in the Class, including private and protected members, but not members of the parent Class, respectively

5.1 Getting properties of a runtime class

  • methods
Field[] getFields()
Filed[] getDeclaredFields()
Copy the code

The getFileds method returns an array of Field objects that record the public properties of this class or its parent; GetDeclaredFileds also returns an array of Field objects that record all the properties of the class.

Note: If there are no attributes defined in the Class, or if the Class object describes a primitive type or an array type, these methods return an array of length 0

  • Code practice
public class TestReflection04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("reflection.People");

        // Get the name of the class
        System.out.println(c1.getName());// Package name + class name
        System.out.println(c1.getSimpleName());/ / the name of the class

        // Get the class attributes
        Field[] fields=c1.getFields(); // Can only find public
        fields=c1.getDeclaredFields();  // All attributes can be found
        for (Field field:fields){
            System.out.println(field);
        }

        // Gets the value of the specified attribute
        Field name=c1.getDeclaredField("name"); System.out.println(name); }}Copy the code

5.2 Methods for obtaining run-time classes

  • methods
Method[] = new getMethods[]
Method[] =new getDeclaredMethods[]
Copy the code

The getMethods Method Method returns an array of Method objects that record the public methods of the class or its parent; GetDeclaredMethods [] also returns an array of Method objects that record all the methods of the class or interface.

  • Code practice
/** * Gets the runtime structure of the class * including methods, attributes, constructors */
public class TestReflection04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("reflection.People");

   
       // Get the class method
        Method[] methods=c1.getMethods();// Get all the public methods of this class and its superclass
        for(Method method:methods){
            System.out.println(method);
        }
        // Get all the methods of this class
        methods=c1.getDeclaredMethods();
        for(Method method:methods){
            System.out.println(method);
        }

        // Get the specified method
        Method getName=c1.getMethod("getName".null); System.out.println(getName); }}Copy the code

5.3 Creating a constructor for the runtime class

  • methods
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
Copy the code

The getConstructors method returns an array of Constructor objects that record the public public Constructor for this class or its parent; GetDeclaredConstructors also returns an array containing Constructor objects, which record all the constructors of the class.

  • Code practice
public class TestReflection04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("reflection.People");
        Constructor[] constructors=c1.getConstructors();
        for (Constructor constructor:constructors){
            System.out.println(constructor);
        }
        constructors=c1.getDeclaredConstructors();
        for (Constructor constructor:constructors){
            System.out.println(constructor);
        }
        // Get the specified constructor
        Constructor declareConstructor=c1.getDeclaredConstructor(int.class,int.class,String.class); System.out.println(declareConstructor); }}Copy the code

6. Dynamically create class objects

In the previous article, we explained how to obtain the runtime structure of a class. If we want to use it, we must create the object of the class

Create an object of the Class by calling the Class object newInstance() method

A class must have a no-argument constructor, because the no-argument constructor is called when an operation is performed if the constructor in the class is not explicitly called, and the constructor is needed to instantiate an object

Class constructor access needs to be sufficient

  • Use the Class getDeclaredConstructor(Class….) ParameTypes) gets a constructor of the specified parameter type for this class
  • Pass an array of objects to the constructor parameters that contain the parameters required by the constructor
  • Instantiate objects by Constructor

The test code

public class TestRelection05 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class c1=Class.forName("reflection.People");
        // Construct an object, essentially calling the no-argument constructor
        People people=(People) c1.newInstance();
        System.out.println(people);

        // Create the object through the constructor
        Constructor constructor=c1.getDeclaredConstructor(int.class,int.class,String.class);
        People people2=(People) constructor.newInstance(23.25."Simon");
        System.out.println(people2);
        // Call normal methods through reflection
        People people3=(People) c1.newInstance();
        Method setName=c1.getDeclaredMethod("setName", String.class);
        //invoke: activate (object, "method value")
        setName.invoke(people3,"Simon Lang");
        System.out.println(people3.getName());

        // Invoke attributes through reflection
        People people4=(People) c1.newInstance();
        Field name=c1.getDeclaredField("name");
        // Private properties cannot be manipulated directly, we need to turn off the program's security monitoring, property or method setAccessible(true)
        name.setAccessible(true);
        name.set(people4,"Simon snow"); System.out.println(people4.getName()); }}Copy the code

setAccessible

  • Method and Field and Constructor objects both setAccessible() methods
  • SetAccessible Enables or disables access security checks
  • A value of true indicates that the reflected object uses this to cancel Java language access checks when used
  • Make private members that would otherwise not be accessible accessible
  • A value of false indicates that the reflected object should perform Java language access checking

It’s too crowded on the mountainside. I want to go to the top