Ref

  • Java Basics and Improved Dry goods series – Java reflection mechanism
  • After reflection, I was admitted | the Denver nuggets
  • Ref – Gets the value of an object property by reflection
  • Ref – Takes attribute values on the generic class T using reflection

Basic

A program that analyzes class abilities is called reflection, and reflective mechanisms can be used

  • Analyze the capabilities of the class at run time, obtaining the properties and methods of the class
  • View objects at run time, for example, writing onetoStringMethod is used by all classes
  • Implement common array operation code
  • Call a Method with a Method object (this object is very similar to a function pointer in C++)

There are four main components of Java reflection

  1. Class
  2. Field
  3. Constructor
  4. Method

Here is a simple example of creating different objects based on class names. In the code, pass java.util.HashMap or java.util.linkedhashMap to create different Map objects.

public Map<Integer, Integer> getMap(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    Class clazz = Class.forName(className);
    return (Map<Integer, Integer>) clazz.newInstance();
}

public static void main(String[] args) throws Exception {
    String className1 = "java.util.HashMap";
    String className2 = "java.util.LinkedHashMap";
    Map<Integer, Integer> map1 = getMap(className1); //HashMap
    Map<Integer, Integer> map2 = getMap(className2); //LinkedHashMap
}
Copy the code

It is important to emphasize here that when you create an object using class.forname (), you pass in the full Class name path. For example, you need to pass in java.util.LinkedHashMap instead of LinkedHashMap, otherwise it will prompt you for the wrong class name, as shown below.

Exception in thread "main" java.lang.ClassNotFoundException: LinkedHashMap
Copy the code

Class Class and Class type

To understand reflection, first understand the Class Class, which is the foundation of reflection implementation.

In Java, each Class has its own Class object. When we write a.java file and use javac to compile it, a.class bytecode file is generated, which contains all the information about the Class, such as properties, constructors, methods

When the bytecode file is loaded into the virtual machine for execution, a Class object is generated in memory that contains all the information inside the Class and can be retrieved while the program is running.

Class is an instance object of the java.lang.Class Class, and Class is a Class named Class of all classes. This is how we create and represent ordinary objects

Code code1 = new Code();
Copy the code

All classes are Class objects

Class c = new Class();
Copy the code

But when we look at the source code for Class, we write it like this

    /* * Private constructor. Only the Java Virtual Machine creates Class objects. * This constructor is not used and prevents the default constructor being * generated. */
    private Class(ClassLoader loader) {
        // Initialize final field for classLoader. The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }
Copy the code

As you can see, the constructor is private, and only the JVM can create Class objects, so you can’t just new a Class object like a normal Class. Although we cannot new a Class object, we can get one from an existing Class. There are three ways to get a Class object

  1. The name of the class. The classThis method can be obtained only if the type of the class has been declared before compilationClassobject
Class clazz = SmallPineapple.class;
Copy the code
  1. Instance. GetClass (): obtained by instantiating the objectClassobject
SmallPineapple sp = new SmallPineapple();
Class clazz = sp.getClass();
Copy the code
  1. Class.forName(className)Through:The fully qualified name of the classGets the class’sClassobject
Class clazz = Class.forName("com.bean.smallpineapple");
Copy the code

Each Class has only one Class object, which is the same as the Class object obtained using the above three methods.

A complete example is given below.

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        1 / / method
        // Specify that any class has an implicit static member variable class, which is obtained by obtaining the class static member variable class
        Class c1 = Code.class;

        2 / / method
        // Code1 is an object of Code obtained through the getClass() method of an object of a class
        Class c2 = code1.getClass();
        
        3 / / method
        // The Class calls the forName method, obtained by the fully qualified name of a Class
        Class c3 = Class.forName("com.trigl.reflect.Code"); System.out.println(c1.getName()); System.out.println(c2.getName()); System.out.println(c3.getName()); }}Copy the code
/ / the execution result com. Tengj. Reflect. ReflectDemo com. Tengj. Reflect. ReflectDemo com. Tengj. Reflect. ReflectDemoCopy the code

Here, c1, c2, and c3 are Class objects, which are exactly the same and have a scientific name called the Class type of Code.

As the name implies, a class type is a class type, which describes what a class is, what a class has, so we can know the properties and methods of a class from a class type, and we can call the properties and methods of a class, and that’s the basis of reflection, right

Now that we know how to get the Class, what can we do with this Class?

  • Get member methodMethod
  • Get member variablesField
  • Get constructorConstructor

The following is a detailed introduction.

Basic use of reflection

Let’s create a concrete entity class to show the basic use of reflection.

public class SmallPineapple {
    public String name;
    public int age;
    private double weight; // Only you know your weight
    
    public SmallPineapple(a) {}
    
    public SmallPineapple(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    public void getInfo(a) {
        System.out.print("["+ name + "The age is:" + age + "]"); }}Copy the code

Construct the instantiation object of the class

There are two ways to construct an instance of a class by reflection

  1. ClassThe objectnewInstance()Method in which an object instance can only be created by calling the class’s default no-argument constructor.
Class clazz = Class.forName("com.bean.SmallPineapple"); SmallPineapple smallPineapple = (SmallPineapple) clazz.newInstance(); smallPineapple.getInfo(); // [null age is: 0]Copy the code
  1. ConstructorConstructor callnewInstance()methods
// The default constructor takes no arguments
Class clazz = Class.forName("com.bean.SmallPineapple");
Constructor constructor = clazz.getConstructor();
SmallPineapple smallPineapple2 = (SmallPineapple) constructor.newInstance("Little pineapple".21);
smallPineapple2.getInfo();
// [null age is: 0]
Copy the code

If clazz.getconstructor () is called without a parameter type, the default no-argument constructor is used to create an object instance.

// Specify the constructor
Class clazz = Class.forName("com.bean.SmallPineapple");
Constructor constructor = clazz.getConstructor(String.class, int.class);
SmallPineapple smallPineapple2 = (SmallPineapple) constructor.newInstance("Little pineapple".21);
smallPineapple2.getInfo();
// [The age of the pineapple is: 21]
Copy the code

clazz.getConstructor(Object… When paramTypes is called, specifying the type of argument to the constructor, the specified constructor is called, as shown in the code above.

Invoke – Invoke a method by reflection

Once an object of a Method class has been obtained by reflection, it can be executed by calling the Invoke Method.

invoke(Oject obj, Object... args)
Copy the code

In this method, parameter 1 specifies the object on which the method is called, and parameter 2 is the parameter list value of the method. If the method being called is static, you only need to pass null for parameter 1, because static methods are not associated with an object, only with a class.

In the following code, instantiate an object by reflection, then get the Method Method object and call invoke() to specify the getInfo() Method of SmallPineapple.

Class clazz = Class.forName("com.bean.SmallPineapple");
Constructor constructor = clazz.getConstructor(String.class, int.class);
constructor.setAccessible(true);
SmallPineapple sp = (SmallPineapple) constructor.newInstance("Little pineapple".21);
Method method = clazz.getMethod("getInfo");
if(method ! =null) {
    method.invoke(sp, null);
}
// [The age of the pineapple is: 21]
Copy the code

Gets the member Method -Method

// Get a single method of the class, not including the parent
public Method getDeclaredMethod(String name, Class
       ... parameterTypes) 

// Get the public methods of the class, including the parent
public Method getMethod(String name, Class
       ... parameterTypes) 

// Get all the methods of the class. The result is returned as an array
public Method[] getDeclaredMethods(a);
public Method[] getMethods();
Copy the code

The following is an example.

class Solution {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        try {
            / / generated Class
            Class c1 = Class.forName("com.lbs0912.java.demo.entity.SmallPineapple");
            // newInstance initializes an instance
            Object o1 = c1.newInstance();
            // Get method
            Method method = c1.getMethod("getInfo", String.class, int.class);
            // The method is invoked with invoke, the first parameter being the instance object followed by the concrete parameter value
            method.invoke(o1, "lbs0912".28);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

The result is as follows

[AGE of LBS0912 is: 28]Copy the code

Sometimes we want to get information about all the member methods in a class, we can do the following.

Class c1 = Class.forName("com.lbs0912.java.demo.entity.SmallPineapple");

Method[] methods = c1.getDeclaredMethods();
for(Method method1:methods){
    String  methodName= method1.getName();
    System.out.println(methodName);
}
Copy the code

Gets the member variable -field

The member variable of the class is also an object, which is an object of java.lang.Reflect. Field, so we get this information through methods encapsulated in java.lang.Reflect. Field.

Get a single member variable, using the following method of the Class Class

// Get all the variables declared by the class itself, excluding the variables of its parent class
public Field getDeclaredField(String name) 

// Get all of the class's public member variables, including its parent
public Field getField(String name) 
Copy the code

The following shows how to get the weight private member variable of an object.

        try {
            Class c1 = Class.forName("com.lbs0912.java.demo.entity.SmallPineapple");
            Field field = c1.getDeclaredField("weight");
            Object o = c1.newInstance();
            // Set whether access is allowed. Since this variable is private, allow access manually
            // If weight is public, this line is not needed
            field.setAccessible(true);
            Object weight = field.get(o);
            System.out.println(weight);
        } catch (Exception e) {
            e.printStackTrace();
        }
Copy the code

The execution result

0.0
Copy the code

Similarly, you can get information about all member variables, as shown below.

Field[] fields = c1.getDeclaredFields();
for(Field field1:fields){
    System.out.println(field.getName() + "-" + field1.getType());
}
Copy the code

The execution result

weight---class java.lang.String
weight---int
weight---double
Copy the code

The above code is the property value obtained by using the field.get(object) method. There are two ways to get the value of an object property through reflection

  1. field.get(object)Through:get()Method to get the value of an attribute
  2. method.invoke(object)Through:invoke()Method to get the value of an attribute

Refer to the link

  • Ref – Gets the value of an object property by reflection
  • Ref – Takes attribute values on the generic class T using reflection

Two entity classes are presented here for subsequent code demonstrations.

@Data
public class User {
    private Duty duty;
}

@Data
public class Duty {
    private String name;
}
Copy the code

Get the property value with the get() method

    /** * Get the value of the field * from the object or its parent class by the field name@paramObject Object instance *@paramFieldName fieldName *@returnThe value corresponding to the field */
    private static Object getValue(Object object,String fieldName){
        if(null == object || StringUtils.isBlank(fieldName)){
            return null;
        }
        Field field = null; Class<? > clazz = object.getClass();for(; clazz ! = Object.class; clazz = clazz.getSuperclass()){// Find the parent class until the parent class is Object
            try{
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(object);
            } catch (Exception e){
                log.error("getValue exception",e); }}return null;
    }
Copy the code
    public static void main(String[] args) {
        Duty duty  = new Duty("testName");
        String nameStr = (String) getValue(duty,"name");
        System.out.println(getValue(duty,"name")); //testName
        System.out.println(nameStr); //testName
    }
Copy the code

Get the property value via the method.invoke() method

In the following code, get the name of the corresponding method using the PropertyDescriptor.

    /** * Get the value of the field from the object or its parent by the field name (call the dictionary's get method) *@paramObject Object instance *@paramFieldName fieldName *@returnThe value corresponding to the field */
    public static Object getValueOfGet(Object object, String fieldName){
        if (object == null || StringUtils.isBlank(fieldName)) {
            return null;
        }
        Field field = null; Class<? > clazz = object.getClass();for(; clazz ! = Object.class; clazz = clazz.getSuperclass()) {try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                // Get the get method
                Method getMethod = pd.getReadMethod();
                The get method returns an Object
                return getMethod.invoke(object);

                // where the statement is equivalent to
                // Method method = clazz.getMethod("getName");
                // return method.invoke(object);

            } catch (Exception e) {
                log.error("getValue exception",e); }}return null;
    }
Copy the code
    public static void main(String[] args) {
        Duty duty  = new Duty("testName");
        System.out.println(getValueOfGet(duty,"name"));
    }
Copy the code

If you do not use PropertyDescriptor, you can simply use the get + property name to get the method name.

    private static Method getReadMethod(String name, Class
        clazz) {
        String methodName = "get" + ProString.upperFirst(name);
        try {
            return clazz.getDeclaredMethod(methodName);
        } catch (NoSuchMethodException | SecurityException e) {
            return null; }}Copy the code

Get constructor

The Constructor of a class is an object, it is a Java. Lang. Reflect. The Constructor of an object, so we through the Java. Lang. Reflect. Encapsulated inside the Constructor method to get the information.

Get a single constructor, implemented through the following methods of the Class Class

// Get all constructors for the class, excluding the parent class's constructors
public Constructor<T> getDeclaredConstructor(Class
       ... parameterTypes) 
// Get all the public constructors for the class, including the parent class
public Constructor<T> getConstructor(Class
       ... parameterTypes) 
Copy the code

An example is shown below.

    try {
        Class c1 = Class.forName("com.lbs0912.java.demo.entity.SmallPineapple");
        Constructor constructor = c1.getConstructor(String.class,int.class);
        // Set whether access is allowed. Since the constructor is private, allow access manually
        // This line is not needed if the constructor is public
        constructor.setAccessible(true);
        Object o1 = constructor.newInstance("lbs0912".28);
    } catch (Exception e) {
        e.printStackTrace();
    }
Copy the code

Reflection application scenarios

Common application scenarios of reflection include

  • Spring instantiation object: When the program starts, Spring reads the configuration fileapplicationContext.xmlAnd instantiate all the tags inside into the IOC container.
  • Reflection + Factory pattern: Eliminate multiple branches in the factory by reflection. If you need to produce new classes, you don’t need to worry about the factory class. The factory class can handle all kinds of new classes.
  • JDBC Connection to a database: When JDBC is used to connect to a database, the reflection load driver class is used to specify the driver class for connecting to the database

Spring’s IOC container

In Spring, you will often write a context configuration file, applicationContext.xml, which contains bean configuration. When the program starts, it reads the.xml file, parses out all the

tags, and instantiates objects into the IOC container.


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="smallpineapple" class="com.bean.SmallPineapple">
        <constructor-arg type="java.lang.String" value="Little pineapple"/>
        <constructor-arg type="int" value="21"/>
    </bean>
</beans>
Copy the code

After the above documents is defined through the ClassPathXmlApplicationContext loading the configuration file, the program starts, the Spring will all the bean is instantiated in the configuration file, in the IOC container, the IOC container is essentially a factory, The factory passes in the ID attribute of the

tag to get the corresponding instance.

public class Main {
    public static void main(String[] args) {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        SmallPineapple smallPineapple = (SmallPineapple) ac.getBean("smallpineapple");
        smallPineapple.getInfo(); // [The age of the pineapple is: 21]}}Copy the code

Spring’s instantiation process is simplified to reflect the steps of instantiating an object

  1. To obtainClassObject constructor
  2. Called through the constructornewInstance()Instantiate an object

Of course, Spring does a lot of extra work when instantiating objects to make current development easy and stable.

Reflection trap point

advantages

  • Increased program flexibility: Flexibility to instantiate different objects in the face of changing requirements

disadvantages

  • Breaks class encapsulation: access can be forcedprivateEmbellished information
  • Performance loss: Reflection requires far more checking and parsing steps than directly instantiating objects, calling methods, and accessing variables that the JVM cannot optimize for.

Break the encapsulation of classes

In reflection, a call to setAccessable(true) forces external access to information about a private modifier, regardless of access modifier restrictions.

The following is illustrated with specific codes.

public class SmallPineapple {
    public String name;
    public int age;
    private double weight; // Only you know your weight
    
    public SmallPineapple(String name, int age, double weight) {
        this.name = name;
        this.age = age;
        this.weight = weight; }}Copy the code

The weight attribute of the SmallPineapple object is private. In reflection, setAccessable(true) is called to make the property accessible from the outside world.

SmallPineapple sp = new SmallPineapple("Little pineapple".21."54.5");
Clazz clazz = Class.forName(sp.getClass());
Field weight = clazz.getDeclaredField("weight");
weight.setAccessable(true);
System.out.println("The weight of the pineapple is:" + weight.get(sp));
// The small pineapple weighs 54.5 kg

Copy the code