Reflection technology is one of the core Java technology, although we daily development, basically may not use much, but it is also a must learn. Because many frameworks are designed with reflection in mind, this means reflection is an important technology for us to move forward.

 

What is reflection?

First let’s look at some plain calling code

Start by creating a plain Person class with a plain work method

package com.lzh.reflect;


public class Person {
    public void work(String content) { System.out.println(content); }}Copy the code

Create another Test class to call

public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        person.work("Coding"); }}Copy the code

The result is no longer pasted, just the string “encoded”, printed to the console.

 

Now we use reflection to do the same call:

public class Test {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, ClassNotFoundException {
        Class clz = Class.forName("com.lzh.reflect.Person");
        Method method = clz.getMethod("work",String.class);
        Constructor constructor = clz.getConstructor();
        Object object = constructor.newInstance();
        method.invoke(object,"Coding"); }}Copy the code

??

Although this code is bloated and complex, it performs exactly the same as the normal call above. The most obvious difference is that reflection calls the same method of the same class without specifying a class. This sounds a bit convoluted, but if your Person and Test files are in different packages, you can see that a normal call would have to import com.lzh.reflect.person; , import the Person class. But if it’s reflection, you don’t need to import a specific package, but rather determine which class it is from the classpath string in the runtime analysis parameter.

From the coder’s point of view, even if reflection is used, we know which class is used and which method is called, but for the JVM, they are not known until this point. At compile time, the class’s pathname and method name are just string arguments to it.

With reflection we can even change the internal call logic of the server by modifying the configuration or passing specific parameters without rebooting the server, so this is also a security risk. Be careful with external input parameters when using reflection.

 

Summary: After a normal call is compiled, it knows which class to load and which method of that class to call. But reflection knows what class to operate on at run time, gets one of the class’s constructors to build the object at run time, and then calls one of the class’s methods.

 

 

How to use reflection

1. Access to the Class

First we need to get an object of class by reflection

Class. ForName static method

The first method is the one we used in the previous example. Use class.forname (the full path of the Class) to specify the Class. This way we need to know the full path name of the Class. If the path is incorrect and the Class cannot be loaded, a ClassNotFoundException will be thrown.

Class clz = Class.forName("com.lzh.reflect.Person");
Copy the code
(2). A class method

This method needs to import the class, specify the name of the class, it looks like a bit of pants farts, because reflection is only used because you can’t determine the class. But it can be used if we’re focused on which method or constructor reflection calls, and the Class is already defined, so we don’t need to spend time on the Class.

Class clz = Person.class;
Copy the code
③ Object getClass() method

This is a simpler way to get the class directly from the object, which is more specific than method 2. This time, we don’t even need to worry about the constructor. Again, this might be useful if we just focus on method.

Person person = new Person();
Class clz1 = person.getClass();
Copy the code
④ The Type attribute of the base Type

If it is a base Class, we can get the Class of its base Class by using the TYPE attribute of the corresponding wrapper Class. Each base TYPE has this TYPE attribute.

Class byteClass = Byte.TYPE; Class shortClass = Short.TYPE; Class integerClass = Integer.TYPE; Class longClass = Long.TYPE; Class charClass = Character.TYPE; Class floatClass = Float.TYPE; Class doubleClass = Double.TYPE; Class boolClass = Boolean.TYPE; Or Class byteClass equalsbyte.class;
Class shortClass = short.class;
Class integerClass = int.class;
Class longClass = long.class;
Class charClass = char.class;
Class floatClass = float.class;
Class doubleClass = double.class;
Class boolClass = boolean.class;
Copy the code

Class can be written simply as int.class or byte.class, and the effect is the same. Decompilation shows that int.class is actually integer.type.

Note, however, that this is not the same thing as integer. class. The class type of the base type and the class type of the wrapper class are two different things; they are not the same thing.

(5) Class

We can also get the subclass object by using getSuperclass().

The parent class of Student is Person:

Student student = new Student();
Class slz = student.getClass();
Class superclass = slz.getSuperclass();
Copy the code

 

2. Obtain objects

Once we get the Class, we naturally need to get the object of that Class through some constructor

① Pass newInstance() to the Class object

Get the instance directly from the class, but the principle is to call the parameterless constructor, so if the class has no parameterless constructor, the InstantiationException will be thrown.

Class clz = Person.class;
Person person = (Person) clz.newInstance();
Copy the code
② newInstance() via the Constructor object

This is the way to do it if we want to call the specified constructor. Add a random constructor to the Person class:

public class Person {
    // there is a parameter constructor
    public Person(String name) {
        System.out.println(name);
    }
    public void work(String content) { System.out.println(content); }}Copy the code

Clz.getconstructor (string.class) gets the constructor for the specified arguments (no arguments if the constructor has no arguments), which is also the class, and calls the constructor via the constructor’s newInstance() method. We also need to pass in the arguments we declared earlier:

Class clz = Person.class;
Constructor constructor = clz.getConstructor(String.class);
Person person = (Person) constructor.newInstance("123");
Copy the code

Note that the newInstance() method is a method in the Constructor object, not the same method as the previous Class, so it throws a wider variety of exceptions as well.

③ Private constructor

If the constructor is private, add a private constructor to the Person class:

    private Person(Integer age) {
        System.out.println(age);
    }
Copy the code

If you still use the previous getConstructor(integer.class); A NoSuchMethodException was thrown and the private constructor could not be found because normal getConstructor only gets a public constructor.

All we need to do is replace getConstructor() with getDeclaredConstructor(), adding the Declared keyword to read all Declared constructors so that even private ones can be found. It is also necessary to set setAccessible to true to turn off the access check for reflection objects, otherwise private constructors cannot be accessed.

Class clz = Person.class;
Constructor constructor = clz.getDeclaredConstructor(Integer.class);
constructor.setAccessible(true);
Person person = (Person) constructor.newInstance(18);
Copy the code

 

3. Obtain member variables

First, I’ll pull out the Person class and give it two variables, one of which will be private. Also, to see the results, I regenerated the toString method.

public String name;
private Integer age;


@Override
public String toString(a) {
return "Person{" + "name='" + name + '\' ' + ", age=" + age + '} ';
}
Copy the code
① Use getField() to get a member variable

GetField () gets the specified member variable, getName gets the name of the member variable, and getType gets the type of the member variable

Class clz = Person.class;
Field field = clz.getField("name");
System.out.println(fields.getName());// Get the name
System.out.println(fields.getType());// Get the type
Copy the code

To get a value, pass in an object and call the get method directly:

// Initialize a Person object and assign a value to the member variable
Person person = new Person();
person.name = "Zhang";


// Fetch the value of this member variable by reflection
Class clz = person.getClass();
Field field = clz.getField("name");// Specify field as the name variable
System.out.println(field.get(person));// Get the name member variable value of the passed object person
Copy the code
② Set the value of the member variable to the object through field.set()

Just like get, set is passed a Person object, otherwise how would it know which object to assign a value to? Here we try to construct an object with reflection and reflect the set variable property to reflect get:

Class clz = Person.class;    / / the Class declaration
Constructor clzConstructors = clz.getConstructor();    // Get the default no-argument constructor
Object person = clz.newInstance();// Initialize the Person object


// Get the member attribute and set the value
Field field = clz.getField("name");
field.set(person, "Mad");
// Outputs the properties of the object
System.out.println(field.get(person));
Copy the code

You might think you’re using the same Field object and then you set it and then you get it. In fact, even if you re-initialize a Field object to get before the get step, the result is the same. A field is not bound to an object.

③ Private member variables

As with the constructor, we can get privately-level member variables simply by adding Declared and using getDeclaredField(). Remember that methods that are not Declared can only access the public level, whether constructors, member variables, or methods.

And both require setAccessible(true); To turn off access-level checks:

Field field = clz.getDeclaredField("age");
field.setAccessible(true);
field.set(person, 18);
System.out.println(field.get(person));
Copy the code

 

4. Get and invoke methods

Finally, we get to the core, where we invoke methods using reflection

GetMethod () gets the method and invoke the method

Here I take the liberty of taking the example from the beginning of the previous article directly, using the ordinary work method

    public void work(String content) {
        System.out.println(content);
    }
Copy the code

This time, we should be able to make sense of the call

Class clz = Class.forName("com.lzh.reflect.Person");
Method method = clz.getMethod("work",String.class);    // Get the work(String) method
Constructor constructor = clz.getConstructor();    // Get the constructor
Object object = constructor.newInstance();    // Get the object instance
method.invoke(object,"Coding");    // Call the work method of the object, passing in arguments
Copy the code

Clz.getmethod (“work”, string.class); If the work method has multiple overloads, reflection will not find the specific method.

Method.invoke (object,” coding “) is invoked last; The first argument is the work method to execute, similar to the getSet method of the field object. The second argument is the required number of arguments for the work method. Otherwise, IllegalArgumentException is thrown.

② Call a private method

As you can probably guess, getDeclaredMethod() accesses all methods, including private methods. Remember that if data is not Declared then only public is allowed. So usually we just use getDeclaredMethod, safe.

And remember method.setaccessible (true).

Then we do the whole reflection, importing the Person class without import, and calling the method completely once

The Person class:

public class Person {
   
    public String name;
    private Integer age;    // Private attribute age
   
    public Person(String name) {    // Public parameter constructor
        this.name = name;
    }
   
    private String play(String game) {        // A private parameter method that uses a member variable
        return this.name+"("+this.age+")"+"Play came here." "+ game; }}Copy the code

Test method called:

// Get Class by reflection
Class clz = Class.forName("com.lzh.reflect.Person");


// Get the parameterized constructor, which initializes with the constructor name
Constructor constructor = clz.getDeclaredConstructor(String.class);


// Get the instance and initialize the name attribute
Object person = constructor.newInstance("Billy");


// Manually get the age member attribute and set the value
Field field = clz.getDeclaredField("age");
field.setAccessible(true);
field.set(person, 28);


// Call the method
Method method = clz.getDeclaredMethod("play", String.class);
method.setAccessible(true);
Object invoke = method.invoke(person,"Wrestling");
System.out.println(invoke);
Copy the code

Run the output this time:

Billy (28) play wrestledCopy the code

 

*5. Some other methods

A brief description of a few other methods related to reflection is optional and can be skipped.

① Get constructor, method, variable plus s

GetConstructor () and getDeclaredConstructor() get the constructor, getMethod() and getDeclaredMethod() get the method, GetField () and getDeclaredField() are fetch properties.

But if you add an S to the end of each of these method names, you get all the constructors/methods/properties,

The return type is an array of type Constructor or Method or Field

Constructor[] clzConstructors = clz.getConstructors();
Constructor[] declaredConstructors = clz.getDeclaredConstructors();


Field[] fields = clz.getFields();


Method[] methods = clz.getMethods();
Copy the code
(2) getModifiers ()

Constructor, Method, and Field objects all have a Method getModifiers() that returns an integer value representing the sum of the modifiers preceding the current Method or property: No modifier is 0, public is 1, private is 2, protected is 4, static is 8, and final is 16. For example, a method whose modifier is public static final adds up to 25.

The return type is: int

This method returns only one number, but the calculation is always accurate to get the modifier, because each number is a multiple of 2, so only one result is computed for each number returned.

And we don’t need to calculate, reflection provides the Modifier. ToString () method to convert this number to modifiers:

System.out.println("Modifier:"+Modifier.toString(method.getModifiers()));
Copy the code
(3) getParameterTypes ()

GetParameterTypes () returns a list of the arguments to the current Method in array-based Class format.

The return type is: Class<? > []

System.out.println(Arrays.toString(work.getParameterTypes()));
Copy the code

 

 

Third, summary

So far, as long as we look at it carefully and try it out for ourselves, we can at least use it, if not learn it.

It’s also a reflection that I didn’t learn until I was in the job for a year, because I didn’t use it that much in the job, well, not at all.

But when we look at the source code of some awesome framework code, we will find that there are many places to use reflection, for example, we are familiar with Spring, its core IOC and AOP functions also rely on reflection. So even if just to read the source code, in order to deal with the interviewer, we should also learn to reflect, learn to reflect everyone is the best.

 

 

 

References:

Baymax: Java Reflection: Getting started, Using, and Working Principles

www.cnblogs.com/chanshuyi/p…

Java reflection technology in detail

Blog.csdn.net/huangliniqn…