πŸŽ“ do your best and listen to fate. The blogger is studying for a master’s degree in Southeast University. She loves fitness and basketball. She is willing to share what she sees and gets related to technology

🎁 This article has been included in CS-wiki (Gitee official recommendation project, now 1.0K + STAR), is committed to creating a perfect back-end knowledge system, less detour on the road of technology, welcome friends to come to exchange and learn

Java reflection mechanism for small white, is really a big snag, other things, is content more, see more back back good, more reflection is really no matter how many times to see don’t understand or don’t understand, the major and the inside of the school teaching material should be no reflection, this chapter is about. To be honest, before this article, I did not know all about reflection, after all, it is rarely used in common development, but after reading this article, I believe you will not have any doubts about reflection.

The mind map of the full text is as follows:

1. Throw in the towel: Why use reflection

As mentioned earlier, the use of interfaces improves the maintainability and extensibility of your code and reduces the coupling of your code. Here’s an example:

First, we have an interface X and its method test, and two corresponding implementation classes A and B:

public class Test {
    
    interface X {
    	public void test(a);
	}

    class A implements X{
        @Override
        public void test(a) {
             System.out.println("I am A"); }}class B implements X{
        @Override
        public void test(a) {
            System.out.println("I am B"); }}Copy the code

In general, we need to use a new implementation class. Look at this code:

public class Test {...public static void main(String[] args) {
        X a = create1("A");
        a.test();
        X b = create1("B");
        b.test();
    }

    public static X create1(String name){
        if (name.equals("A")) {
            return new A();
        } else if(name.equals("B")) {return new B();
        }
        return null; }}Copy the code

If there are hundreds or thousands of different X implementations to be created, wouldn’t we need to write thousands of if statements to return different X objects?

Let’s take a look at how the reflex mechanism works:

public class Test {

    public static void main(String[] args) {
		X a = create2("A");
        a.test();
        X b = create2("B");
        b.testReflect();
    }
    
	// Use the reflection mechanism
    public static X create2(String name){ Class<? >class = Class.forName(name);
        X x = (X) class.newInstance();
        returnx; }}Copy the code

Pass in the package name and class name to the create2() method, dynamically load the specified class through the reflection mechanism, and then instantiate the object.

After reading the above example, I believe you have a certain understanding of reflection. Reflection has the following four functions:

  • The class to which any object belongs is known at run time (dynamically compiled).
  • Construct an object of any class at run time.
  • The member variables and methods that any class has are known at runtime.
  • Calls the methods and properties of any object at run time.

This ability to dynamically retrieve information and dynamically invoke methods on objects is called the Reflection mechanism of the Java language.

2. Understand Class

To understand reflection, you must first understand the Class Class, because the Class Class is the basis of the reflection implementation.

For the duration of a program’s run, the JVM maintains a type identity for all objects called the runtime, which tracks the complete structural information of the class to which each object belongs, including package name, class name, implemented interface, owned methods, and fields. This information is accessible through a specialized Java Class, Class. We can think of Class as the type of Class, a Class object, called a Class type object, a Class object corresponds to a.class file loaded into the JVM.

In general, the class must come before the object. For example, in the following code, the normal class loading process looks like this:

import java.util.Date; / / first class

public class Test {
    public static void main(String[] args) {
        Date date = new Date(); // After the objectSystem.out.println(date); }}Copy the code

First, the JVM compiles your code into a.class bytecode file, which is then loaded into the JVM’s memory by a class Loader. At the same time, it creates a class object of class Date and stores it in the heap. But the type object of the class). Before creating a Date object, the JVM checks to see if its Class is loaded, finds a Class object corresponding to the Class, allocates memory for it, and then initializes new Date().

Note that there is only one Class object per Class, that is, if we have a second new Date() statement, the JVM will not regenerate a Class object of Date, because one already exists. This also allows us to compare two class objects using the == operator:

System.out.println(date.getClass() == Date.getClass()); // true
Copy the code

OK, so after loading a Class, the method area of the heap memory produces a Class object. This object contains the complete structure information of the Class. We can see the structure of the Class through this Class object, like a mirror. So we figuratively call it: reflection.

Be more specific. Explain it a little bit more. As mentioned above, in general, the class must come before the object, and we call this general case “positive”. So the “inverse” in reflection can be understood as finding the class (where the object came from) based on the object.

Date date = new Date();
System.out.println(date.getClass()); // "class java.util.Date"
Copy the code

By reflection, after calling getClass(), we get the Class object corresponding to the Date Class, see the structure of the Date Class, print the full name of the Class to which the Date object belongs, and find the source of the object. Of course, there’s more than one way to get a Class object.

3. Four ways to obtain Class objects

The Class constructor is private, which means that only the JVM can create an object of the Class. We cannot simply create a new Class object like a normal Class.

We can only get a Class object from an existing Class. Java provides four ways to do this:

If you know the specific class, you can use:

Class alunbarClass = TargetObject.class;
Copy the code

However, we generally do not know the concrete Class, basically through the package under the Class to obtain the Class object, through this way to obtain the Class object will not be initialized.

Class.forname () ΒΆ

Class alunbarClass1 = Class.forName("com.xxx.TargetObject");
Copy the code

This method actually calls forName0 internally:

The second Boolean argument indicates whether the class needs to be initialized. The default is yes. Once initialized, the static block of the target object is triggered and the static arguments are initialized again.

GetClass () : getClass();

Date date = new Date();
Class alunbarClass2 = date.getClass(); // Get the Class object of the instance of this object
Copy the code

Fourth: through class loadersxxxClassLoader.loadClass()Pass in the classpath to get

class clazz = ClassLoader.LoadClass("com.xxx.TargetObject");
Copy the code

Obtaining a Class object through a classloader is not initialized, meaning that static blocks and static objects are not executed without a series of steps including initialization. So here’s a comparison with forName.

4. Construct an instance of the class using reflection

We have shown how to obtain an object of Class, so after successful acquisition, we need to construct an instance of the corresponding Class. There are three methods, the first is the most common, and the last one you should know a little bit about.

1) use the Class. NewInstance

Here’s an example:

Date date1 = new Date();
Class alunbarClass2 = date1.getClass();
Date date2 = alunbarClass2.newInstance(); // Create an instance of the same class type as alunbarClass2
Copy the code

Creates an instance with the same class type as alunbarClass2.

Note that the newInstance method calls the default constructor (which takes no arguments) to initialize the newly created object. If the class does not have a default constructor, an exception will be thrown.

(2) Get the constructor by reflection and then call it

Since not all classes have parameterless constructors or Class constructors are private, class. newInstance cannot be satisfied if we want to instantiate objects through reflection.

In this case, we can implement this using Constructor’s newInstance method, which first gets the Constructor and then executes it.

It’s easy to see from the above code that Constructor. NewInstance can take arguments, while class.newinstance does not, which is why it can only call constructors with no arguments.

Don’t confuse these two newInstance methods. If the constructor of the Class being called is the default constructor, class.newinstance () is a good choice. Constractor.newinstance () Is used to call constractor.newinstance ()

Constructor. NewInstance is the method that executes the Constructor. Let’s take a look at some of the ways in which constructors can be obtained. The following methods are easy to remember and understand, and the return value is received by the Cnostructor type.

Batch fetching constructors:

1) Get all “public” constructors

public Constructor[] getConstructors() { }
Copy the code

2) Get all constructors (private, protected, default, public)

public Constructor[] getDeclaredConstructors() { }
Copy the code

A single fetching constructor:

1) Get a “public” constructor for the specified parameter type

public Constructor getConstructor(Class... parameterTypes) {}Copy the code

2) Get a “constructor” of the specified parameter type, which can be private, or protected, default, public

public Constructor getDeclaredConstructor(Class... parameterTypes) {}Copy the code

Here’s an example:

package fanshe;

public class Student {
	// (default constructor)
	Student(String str){
		System.out.println("The (default) constructor s =" + str);
	}
	// The constructor has no parameters
	public Student(a){
		System.out.println("The public, parameterless constructor is called to execute...");
	}
	// A constructor with one parameter
	public Student(char name){
		System.out.println("Name:" + name);
	}
	// Constructor with multiple arguments
	public Student(String name ,int age){
		System.out.println("Name:"+name+Age:+ age);// There is a problem with the efficiency of this implementation.
	}
	// Protected constructor
	protected Student(boolean n){
		System.out.println("Protected constructor n =" + n);
	}
	// Private constructor
	private Student(int age){
		System.out.println("Private constructor age:"+ age); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -public class Constructors {
	public static void main(String[] args) throws Exception {
		// Load the Class object
		Class clazz = Class.forName("fanshe.Student");
        
		// Get all public constructors
		Constructor[] conArray = clazz.getConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
        
		// Get all constructors (private, protected, default, public)
		conArray = clazz.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
        
		// Get the public, parameterless constructor
        // The type is null because it is a constructor with no parameters
		// Returns the class object that describes the constructor that takes no arguments.
		Constructor con = clazz.getConstructor(null);
		Object obj = con.newInstance(); // Call the constructor
		
		// Get the private constructor
		con = clazz.getDeclaredConstructor(int.class);
		System.out.println(con);
		con.setAccessible(true); // In order to call the private method/field we need to cancel the security check
		obj = con.newInstance(12); // Call the constructor}}Copy the code

β‘’ Use the open source library Objenesis

Objenesis is an open source library and, like the second method above, can call any constructor, but the wrapper is more concise:

public class Test {
    // There are no parameterless constructors
    private int i;
    public Test(int i){
        this.i = i;
    }
    public void show(a){
        System.out.println("test..."+ i); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -public static void main(String[] args) {
        Objenesis objenesis = new ObjenesisStd(true);
        Test test = objenesis.newInstance(Test.class);
        test.show();
    }
Copy the code

Very simple to use, Objenesis is implemented by the subclass ObjenesisObjenesisStd. Detailed source code here will not delve into, understand can.

5. Obtain member variables by reflection and use them

Similar to getting constructors, getting member variables can be done in batches or individually. The return value is received by the Field type.

Batch obtain:

1) Get all public fields

public Field[] getFields() { }
Copy the code

2) Get all fields (private, protected, default)

public Field[] getDeclaredFields() { }
Copy the code

Single obtain:

1) Get a public field with a given name

public Field getField(String name) {}Copy the code

2) Get a field with a specified name, which can be private, protected, or default

public Field getDeclaredField(String name) {}Copy the code

Once you get member variables, how do you modify their values?

The set method takes two arguments:

  • Obj: which object is modifying this member variable
  • Value: specifies the value to be modified

Here’s an example:

package fanshe.field;

public class Student {
	public Student(a){}public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString(a) {
		return "Student [name=" + name + ", age=" + age + ", sex=" + sex
				+ ", phoneNum=" + phoneNum + "]"; }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -public class Fields {
    public static void main(String[] args) throws Exception {
        // Get the Class object
        Class stuClass = Class.forName("fanshe.field.Student");
        // Get the public, parameterless constructor
        Constructor con = stuClass.getConstructor();
		
		// Get the private constructor
		con = clazz.getDeclaredConstructor(int.class);
		System.out.println(con);
		con.setAccessible(true); // In order to call the private method/field we need to cancel the security check
		obj = con.newInstance(12); // Call the constructor
        
        // Get all public fields
        Field[] fieldArray = stuClass.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

         // Get all fields (private, protected, default)
        fieldArray = stuClass.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        // Get the public field of the specified name
        Field f = stuClass.getField("name");
        Object obj = con.newInstance(); // Call the constructor to create an instance of the class
        f.set(obj, "Andy Lau"); // Assign a value to the name attribute in the Student object


        // Get the private field
        f = stuClass.getDeclaredField("phoneNum");
        f.setAccessible(true); // Violent reflection, remove the private restriction
        f.set(obj, "18888889999"); // Assign a value to the phoneNum property in the Student object}}Copy the code

6. Obtain member methods by reflection and invoke them

Similarly, there are batch fetching and individual fetching methods for retrieving members. The return value is received by the Method type.

Batch obtain:

1) Obtain all “public methods” (including methods of the parent class, including methods of the Object class)

public Method[] getMethods() { }
Copy the code

2) Get all member methods, including private (not inherited)

public Method[] getDeclaredMethods() { }
Copy the code

Single obtain:

Get a member method with the specified method name and parameter types:

public Method getMethod(String name, Class
       ... parameterTypes)
Copy the code

How do you call the methods once you get them?

The invoke method contains two parameters:

  • Obj: Which object to call this method on
  • Args: The argument passed when the method is called

Here’s an example:

package fanshe.method;
 
public class Student {
	public void show1(String s){
		System.out.println("Called: public, String show1(): s =" + s);
	}
	protected void show2(a){
		System.out.println("Called: protected, no-argument show2()");
	}
	void show3(a){
		System.out.println("Called: the default, no-argument show3()");
	}
	private String show4(int age){
		System.out.println("Called show4(): age = private int with a return value." + age);
		return "abcd"; }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --public class MethodClass {
	public static void main(String[] args) throws Exception {
		// Get the Class object
		Class stuClass = Class.forName("fanshe.method.Student");
        // Get the public, parameterless constructor
        Constructor con = stuClass.getConstructor();
        
		// Get all public methods
		stuClass.getMethods();
		Method[] methodArray = stuClass.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
        
		// Get all methods, including private ones
		methodArray = stuClass.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
        
		// Get the public show1() method
		Method m = stuClass.getMethod("show1", String.class);
		System.out.println(m);
		Object obj = con.newInstance(); // Call the constructor to instantiate a Student object
		m.invoke(obj, "Veal");
		
		// Get the private show4() method
		m = stuClass.getDeclaredMethod("show4".int.class);
		m.setAccessible(true); // Remove the private restriction
		Object result = m.invoke(obj, 20);
		System.out.println(Return value:+ result); }}Copy the code

7. Pros and cons of reflection

Advantages: Flexibility and the ability to dynamically obtain instances of classes at run time.

Disadvantages:

1) Performance bottlenecks: Reflection acts as a series of interpreted actions that tell the JVM what to do, and performance is much slower than direct Java code.

2) Security issues: The reflection mechanism breaks encapsulation because the private methods and fields of the class can be obtained and called through reflection.

8. Classic application scenarios of reflection

Reflection is not used a lot in actual programming, but there are a lot of designs that have to do with it. For example:

  • Dynamic proxy mechanism
  • Connect to the database using JDBC
  • Spring/Hibernate framework (it’s actually about reflection because it uses dynamic proxies)

The next article will explain in detail why dynamic proxies use reflection.

JDBC Connection to database

In JDBC operations, if you want to connect to a database, you must follow the following steps:

  • throughClass.forName()Load the driver for the database (by reflection)
  • throughDriverManagerClass to connect to the database, the parameters include the database connection address, user name, password
  • throughConnectionInterface receive connection
  • Close the connection
public static void main(String[] args) throws Exception {  
        Connection con = null; // The connection object to the database
        // 1. Load the driver through reflection
        Class.forName("com.mysql.jdbc.Driver"); 
        // 2. Connect the database
        con = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/test"."root"."root"); 
        // 3. Close the database connection
        con.close(); 
}
Copy the code

The Spring framework

The reflection mechanism is the soul of the Java framework design. The framework is all wrapped up and we barely have to write it ourselves. In addition to Hibernate, Spring also uses many reflection mechanisms. The most typical one is that Spring loads beans (creates objects) through XML configuration files, which is Spring’s IoC. The process is as follows:

  • Load the configuration file and get the Spring container
  • Use reflection to get a Class instance of a Class based on the string passed in
// Get Spring's IoC container and get the object based on the ID
public static void main(String[] args) {
    // 1. Use the ApplicationContext interface to load the configuration file and get the Spring container
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    // 2. Use the reflection mechanism to obtain a Class instance based on this string
    IAccountService aService = (IAccountService) ac.getBean("accountServiceImpl");
    System.out.println(aService);
}
Copy the code

In addition, Spring AOP, because of its use of dynamic proxies, also uses a reflection mechanism, which I will explain in more detail in the Spring series.

πŸ“š References

  • Java Core Technologies – Volume 1 Basics – 10th edition
  • “Thinking In Java – 4th edition”
  • Dedicated Young Ma – Reflection of Java Basics: blog.csdn.net/sinat_38259…

| flying veal πŸŽ‰ pay close attention to the public, get updates immediately

  • The blogger, who is studying for a master’s degree at Southeast University, runs an official account “Flying Veal” in his spare time, which opened on December 29, 2020/12/29. Focus on sharing computer foundation (data structure + algorithm + computer network + database + operating system + Linux), Java foundation and interview guide related original technology good article. The purpose of this public account is to let you can quickly grasp the key knowledge, targeted. I hope you can support me and grow with veal πŸ˜ƒ
  • And recommend personal maintenance of open source tutorial projects: CS-Wiki (Gitee recommended project, now 1.0K + STAR), is committed to create a perfect back-end knowledge system, less detours on the road of technology, welcome to come to exchange learning partners ~ 😊