Deep Understanding of Java Type Information (Class Objects) and Reflection Mechanism

Related articles:

Deep Understanding of Java Type Information (Class Objects) and Reflection Mechanism

Deep Understanding of Java Enumeration Types (enum)

Deep understanding of Java annotation types (@Annotation)

Understanding the synchronized implementation principle of Java concurrency

Deep understanding of Java Memory Model (JMM) and volatile keywords

Deep Understanding of Java Class Loader

This article mainly focuses on the deep analysis of Class objects in Java, which is very important for the follow-up in-depth understanding of reflection technology. The main contents are as follows:

 

 

Deep Understanding of Class Objects

The concept of RRTI and the role of Class objects

Before we know Class objects, we should first understand a concept. Run-Time Type Identification (RTTI) runtime type recognition has always been a concept in C++ for this term. The expression of RRTI in Java originates from Thinking in Java. Its function is to identify the type and class information of an object at runtime. There are two kinds: traditional RRTI, which is false. The other is the reflection mechanism, which allows us to discover and use type information at runtime. Class class is the corresponding class used to represent runtime type information in Java. Class class is also a real class. It exists in the java.lang package of JDK. Some of the source codes are as follows:

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type, AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.(Private constructs, which can only be created by JVM)
     * 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;
    }

Class class is created after the object is the Class object, note that the Class object represents their own manual writing of class type information, such as creating a Shapes class, then JVM will create a Shapes corresponding Class class Class object, the Class object saves the type information related to the Shapes class. In fact, every class in Java has a Class object. Every time we write and compile a newly created class, a corresponding Class object will be generated and the Class object will be saved in the same name. class file (the compiled bytecode file saves the Class object). Why do we need such a Class object? Well, when we new a new object or refer to a static member variable, the class loader subsystem in the Java Virtual Machine (JVM) loads the corresponding Class object into the JVM, and then the JVM creates the class object we need based on the type information or provides the reference value of the static variable. It should be noted that every class class class written manually, no matter how many instance objects are created, has only one Class object in the JVM, that is, each class has and only one corresponding Class object in memory, which can be understood by the following figure (simple phenomena map in memory):

At this point, we can also draw the following information:

  • Class class is also a kind of class, which is different from class keyword.

  • A class written manually is compiled to produce a Class object, which represents the type information of the class created, and the Class object is stored in a file with the same name. class (bytecode file), such as creating a Shapes class. After compiling the Shapes class, a Class object containing the type information related to the Shapes class is created and saved in the Shapes.class bytecode file.

  • Each class identified by the keyword class has only one corresponding Class object in memory to describe its type information. No matter how many instance objects are created, it is based on a Class object.

  • Class classes only have private constructors, so corresponding Class objects can only be created and loaded by JVM

  • The object function of Class class is to provide or obtain the type information of an object at runtime, which is very important for reflection technology (on reflection later).

Class Object Loading and Acquisition

Class Object Loading

As we mentioned earlier, Class objects are loaded by JVM, so when is it loaded? In fact, all classes are dynamically loaded into the JVM when they are first used. When the program creates the first static member reference to the class, it loads the class that is used (in fact, the bytecode file of the class is loaded). Note that creating a new instance object of the class using the new operator is also treated as a reference to the static member of the class (the constructor is also a class). From this point of view, Java programs are not fully loaded into memory before they start running, and all parts of them are loaded on demand. When using this class, the class loader first checks whether the Class object of this class has been loaded (the instance object of the class is created according to the type information in the Class object). If it has not been loaded, the default class loads. Class objects are saved in the same name. Class files after compilation. When the bytecode files of this class are loaded, they must accept relevant validation to ensure that they are not damaged and contain bad Java code (which is the security mechanism detection of java). After no problem, they will be loaded into memory dynamically, which is equivalent to Cl. The ass object is loaded into memory (after all, the class bytecode file holds the Class object) and can also be used to create all instance objects of the class. Here is a simple example to illustrate when Class objects are loaded (cited from Thinking in Java):

package com.zejian;

class Candy {
  static {   System.out.println("Loading Candy"); }
}

class Gum {
  static {   System.out.println("Loading Gum"); }
}

class Cookie {
  static {   System.out.println("Loading Cookie"); }
}

public class SweetShop {
  public static void print(Object obj) {
    System.out.println(obj);
  }
  public static void main(String[] args) {  
    print("inside main");
    new Candy();
    print("After creating Candy");
    try {
      Class.forName("com.zejian.Gum");
    } catch(ClassNotFoundException e) {
      print("Couldn't find Gum");
    }
    print("After Class.forName(\"com.zejian.Gum\")");
    new Cookie();
    print("After creating Cookie");
  }
}

In the above code, each class Candy, Gum, Cookie has a static statement, which will be executed when the class is first loaded. The purpose of this statement is to tell us when the class is loaded and the result of execution:

inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("com.zejian.Gum")
Loading Cookie
After creating Cookie

Process finished with exit code 0

 

As a result, a new Candy object and Cookie object, the constructor will be invoked, which belongs to the reference of static methods. Class objects of Candy class and Classs objects of Cookie will certainly be loaded. After all, the creation of Candy instance object is based on its Class object. What's interesting is that

Class.forName("com.zejian.Gum");

The forName method is a static member method of the Class class, remembering that all Class objects originate from the Class class, so the methods defined in the Class class are suitable for all Class objects. Here, through the forName method, we can get the corresponding Class object reference of the Gum class. From the print results, calling the forName method causes the Gum class to be loaded (provided that the Gum class has never been loaded).

Class.forName method

In the above case, we also know that the call to the Class.forName() method will return a Class object of the corresponding class, so if we want to get runtime type information of a class and use it, we can call the Class.forName() method to get a reference to the Class object. The advantage of this is that we do not need to get the Class object by holding the instance object reference of the class. The second way is to get a Class object of a class through an instance object, where getClass() inherits from the top-level class Object and returns a Class object reference representing the actual type of the object.

public static void main(String[] args) {

    try{
      //Getting Class Objects of Gum Class through Class.forName
      Class clazz=Class.forName("com.zejian.Gum");
      System.out.println("forName=clazz:"+clazz.getName());
    }catch (ClassNotFoundException e){
      e.printStackTrace();
    }

    //Getting Gum's Class es Object through Instance Object
    Gum gum = new Gum();
    Class clazz2=gum.getClass();
    System.out.println("new=clazz2:"+clazz2.getName());

  }

Note that when calling the forName method, you need to catch an exception named ClassNotFoundException, because the forName method can't detect the existence of the class corresponding to the string it passes by the compiler. It can only be checked when the program runs, and if it doesn't exist, a ClassNotFoundException exception will be thrown.

Class literal constant

There is another way to generate a reference to a Class object in Java, which is the Class literal constant, as follows:

//Getting Class Objects Literally Constant
Class clazz = Gum.class

This method is simpler and safer than the previous two methods. Because the compiler is checked by the compiler, and because it does not need to call the forName method, it is more efficient, because getting a reference to a Class object through a literal method does not automatically initialize the class. What's more interesting is that literal constants can be used not only in general classes, but also in interfaces, arrays and basic data types. This is very helpful in the application of reflection technology to transfer parameters. Reflection technology will be analyzed later. Because basic data types have corresponding basic packaging types, its packaging type has a standard field TY. PE, and this TYPE is a reference, pointing to the basic data type of Class object, its equivalent conversion is as follows, generally preferred to use the form of. class, so as to maintain the unity with the form of ordinary classes.

boolean.class = Boolean.TYPE;
char.class = Character.TYPE;
byte.class = Byte.TYPE;
short.class = Short.TYPE;
int.class = Integer.TYPE;
long.class = Long.TYPE;
float.class = Float.TYPE;
double.class = Double.TYPE;
void.class = Void.TYPE

As mentioned earlier, using literal constants to obtain a reference to a Class object does not trigger class initialization. Here we may need to briefly understand the process of class loading, as follows:

  • Loading: A stage in the class loading process: Finding such bytecode files through a fully qualified class and creating a Class object using the bytecode files

  • Links: Verify the security and integrity of bytecodes, and formally allocate storage space for the static domain in the preparation phase. Note that only static member variables are allocated at this time. There are no instance member variables. If necessary, parse all references to other classes created by this class.

  • Initialization: In the final stage of class loading, if the class has a superclass, it is initialized, and static initializers and static initialization member variables are executed.

From this, we can see that when we get the literal constant Class reference, the trigger should be the loading stage, because the Class object has been created in this stage, it is not difficult to obtain its reference, and there is no need to trigger the last stage initialization of the class. Here is a small example to illustrate this process:

import java.util.*;

class Initable {
  //Compile-time static constants
  static final int staticFinal = 47;
  //Non-scheduled static constants
  static final int staticFinal2 =
    ClassInitialization.rand.nextInt(1000);
  static {
    System.out.println("Initializing Initable");
  }
}

class Initable2 {
  //Static member variables
  static int staticNonFinal = 147;
  static {
    System.out.println("Initializing Initable2");
  }
}

class Initable3 {
  //Static member variables
  static int staticNonFinal = 74;
  static {
    System.out.println("Initializing Initable3");
  }
}

public class ClassInitialization {
  public static Random rand = new Random(47);
  public static void main(String[] args) throws Exception {
    //Literally Constant Acquisition for Class Objects
    Class initable = Initable.class;
    System.out.println("After creating Initable ref");
    //Class initialization without triggering
    System.out.println(Initable.staticFinal);
    //Class initialization triggers
    System.out.println(Initable.staticFinal2);
    //Class initialization triggers
    System.out.println(Initable2.staticNonFinal);
    //The forName method obtains Class objects
    Class initable3 = Class.forName("Initable3");
    System.out.println("After creating Initable3 ref");
    System.out.println(Initable3.staticNonFinal);
  }
}

Implementation results:

After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74

From the output results, it can be found that the Class object of Initable class obtained by literal constant acquisition does not trigger the initialization of Initable class, which also verifies the previous analysis. At the same time, it is also found that the Initable.staticFinal variable is not triggered initialization, because staticFinal belongs to static constants at compilation time, and is optimized by constant propagation at compilation stage. Formula stores the constant staticFinal of the Initable class in a constant pool called NotInitialization class. Later, the reference to the Initable class constant staticFinal is actually translated into the reference to the NotInitialization class's own constant pool. Therefore, after compilation, the reference to the compilation period constant will be obtained in the constant pool of the NotInitialization class, which is also the reference compilation period. An important reason why static constants do not trigger Initable class initialization. However, the Initable.staticFinal2 variable is then invoked to trigger the initialization of the Initable class. Note that although staticFinal2 is modified by static and final, its value cannot be determined at compile time. Therefore, staticFinal2 is not a compile time constant, and the Initable class must be initialized before using this variable. Initable2 and Initable3 are static member variables, not compile-time constants, and references trigger initialization. As for the forName method to get a Class object, initialization is bound to trigger, which was analyzed earlier. All these methods of obtaining Class objects have been analyzed, ok ~. Here we can draw a small conclusion:

  • There are three ways to get the reference of Class object: getClass method inherited from Object class, static method forName of Class class and literal constant method "" class ".

  • The getClass method of instance class and the static method forName of Class class will trigger the initialization stage of class, while the literal constant method of getting Class object will not trigger initialization.

  • Initialization is the last stage of class loading, that is to say, after the completion of this stage, the class is loaded into memory (Class object has been created in the loading stage), at which time all kinds of necessary operations can be carried out on the class (such as new object, calling static members, etc.). Note that at this stage, the Java program code or bytecode defined in the class is really started to execute.

In the initial stage of class loading, the virtual machine specification strictly stipulates that there are only five scenarios in which classes must be initialized:

  • When using new keywords to instantiate objects, read or set static fields of a class (excluding compile-time constants) and call static methods, the initialization process of class loading (the final stage of class loading process) must be triggered.

  • When using reflection package (java. lang. reflection) method to make reflection calls to a class, if the class has not been initialized, it needs to be initialized first, which is very important for reflection.

  • When a class is initialized, if its parent class has not been initialized, the initialization of its parent class must be triggered first.

  • When the Java virtual machine starts up, the user needs to specify a main class to be executed (including the main method class), which is initialized by the virtual opportunity first.

  • When using JDK 1.7 dynamic language support, if a java.lang.invoke.MethodHandle instance resolves to a method handle of REF_getStatic, REF_putStatic, and REF_invokeStatic, and if the corresponding class is not initialized, its initialization must be triggered. (If you don't understand this, this is the new dynamic language support of 1.7, the key feature is that it's REF_getStatic, REF_putStatic, REF_invokeStatic. The main process of type checking is at runtime rather than compile time. This is a bigger topic. Let's stop here for a moment.

Understanding Generalized Class Object References

Since the total number of Class references points to a Class object of a class, instance classes can be created using Class objects, which is enough to illustrate the exact type of object that the reference of Class objects points to. With the introduction of generics in Java SE5, we can use generics to represent more specific types of Class objects, even if they are erased during runtime, but compile time is enough to ensure that we use the correct object types. As follows:

/**
 * Created by zejian on 2017/4/30.
 * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original]
 */
public class ClazzDemo {

    public static void main(String[] args){
        //No generics
        Class intClass = int.class;

        //Class object with generic type
        Class<Integer> integerClass = int.class;

        integerClass = Integer.class;

        //Without generic constraints, you can assign values at will
        intClass= double.class;

        //Compile-time error, unable to compile through
        //integerClass = double.class
    }
}

As you can see from the code, declaring a common Class object does not check whether the exact type of Class object meets the requirements in the compiler, and if there are errors, it will only be exposed at runtime. However, by specifying the type of Class object by generic declaration, the compiler will perform additional type checks on generic classes at compile time to ensure that the type is correct at compile time. In fact, Integer.class is an object of Class < Integer > class. It may be confusing to face the following statement, but it can't be compiled and passed.

//Compilation failed
Class<Number> numberClass=Integer.class;
  • 1
  • 2

We might think that Integer is not a subclass of Number. However, the fact is not so simple. After all, Integer's Class object is not a subclass of Number's Class object. As mentioned earlier, all Class objects only come from Class class, which seems to be the case. Of course, we can use wildcard character "?" to solve the problem:

Class<?> intClass = int.class;
intClass = double.class;

There's no problem with such statements. After all, wildcards indicate that all types are applicable, so why not use Class directly and Class<?>? The advantage of this is to tell the compiler that we do use any type of generics, rather than forgetting to use generic constraints, so Class <?> is always better than using Class directly, at least the former does not produce warning messages when the compiler checks. Of course, we can also use the extends keyword to tell the compiler to receive a certain type of subclass, such as solving the previous Number and Integer problems:

//Compiled through!
Class<? extends Number> clazz = Integer.class;
//Give other types
clazz = double.class;
clazz = Number.class;

The above code works, and the extends keyword tells the compiler that any subclass of Number can be assigned. This is different from the previous direct use of Class < Number >. In fact, it should always be remembered that adding generic constraints to Class references is only to provide compile-time type checks to avoid extending errors to runtime.

On Type Conversion

In many scenarios where forced type conversions are required, we are more likely to directly force type conversions:

package com.zejian;

/**
 * Created by zejian on 2017/4/30.
 * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original]
 */
public class ClassCast {

 public void cast(){

     Animal animal= new Dog();
     //Coercive transformation
     Dog dog = (Dog) animal;
 }
}

interface Animal{ }

class Dog implements  Animal{ }

This is due to RRTI. In Java, all types of conversion are checked at run time. RRTI is used to determine whether the type is correct to ensure the completion of the forced conversion. If the type conversion fails, an exception of type conversion will be thrown. In addition to mandatory conversion, a new type conversion method using Class objects is added in Java SE5, as follows:

Animal animal= new Dog();
//These two sentences are equivalent to Dog dog = animal.
Class<Dog> dogType = Dog.class;
Dog dog = dogType.cast(animal)

Using the cast method of Class object, its parameters receive a parameter object and convert it to the type of Class reference. This approach seems to be more cumbersome than previous forced conversions, and indeed, ClassCastException exceptions are thrown when the type cannot be converted correctly. The source code is as follows:

public T cast(Object obj) {
    if (obj != null && !isInstance(obj))
         throw new ClassCastException(cannotCastMsg(obj));
     return (T) obj;
  }

instanceof keyword and isInstance method

With regard to the instanceof keyword, it returns a boolean type value, intended to tell us whether the object is an instance of a particular type. As follows, we use instanceof to detect whether obj is an instance object of Animal type before coercion. If we return true and then type conversion, we can avoid throwing ClassCastException.

public void cast2(Object obj){
    if(obj instanceof Animal){
          Animal animal= (Animal) obj;
      }
}

The isInstance method is a Native method in Class class. It is also used to judge the type of object. Let's take a simple example.

public void cast2(Object obj){
        //instanceof keyword
        if(obj instanceof Animal){
            Animal animal= (Animal) obj;
        }

        //isInstance method
        if(Animal.class.isInstance(obj)){
            Animal animal= (Animal) obj;
        }
  }

In fact, instanceOf and isInstance methods produce the same results. For instance Of, keywords are used only for object reference variables, checking whether the left object is an instantiation of the right class or interface. If the object under test is null, the test result is always false. General form:

//Determine if the object is of this type?
obj.instanceof(class)

 

The isInstance method is the Native method of the Class class, where obj is the object or variable under test, and returns true if obj is an instance of the class or interface calling the method. If the object being detected is null or primitive type, the return value is false; the general form is as follows:

//Determine if the object can be transformed into this class
class.inInstance(obj)

Finally, a simple example is given to verify the equivalence between isInstance method and instanceof method.

class A {}

class B extends A {}

public class C {
  static void test(Object x) {
    print("Testing x of type " + x.getClass());
    print("x instanceof A " + (x instanceof A));
    print("x instanceof B "+ (x instanceof B));
    print("A.isInstance(x) "+ A.class.isInstance(x));
    print("B.isInstance(x) " +
      B.class.isInstance(x));
    print("x.getClass() == A.class " +
      (x.getClass() == A.class));
    print("x.getClass() == B.class " +
      (x.getClass() == B.class));
    print("x.getClass().equals(A.class)) "+
      (x.getClass().equals(A.class)));
    print("x.getClass().equals(B.class)) " +
      (x.getClass().equals(B.class)));
  }
  public static void main(String[] args) {
    test(new A());
    test(new B());
  } 
}

Implementation results:

Testing x of type class com.zejian.A
x instanceof A true
x instanceof B false //A parent class is not necessarily a type of a subclass
A.isInstance(x) true
B.isInstance(x) false
x.getClass() == A.class true
x.getClass() == B.class false
x.getClass().equals(A.class)) true
x.getClass().equals(B.class)) false
---------------------------------------------
Testing x of type class com.zejian.B
x instanceof A true
x instanceof B true
A.isInstance(x) true
B.isInstance(x) true
x.getClass() == A.class false
x.getClass() == B.class true
x.getClass().equals(A.class)) false
x.getClass().equals(B.class)) true

So far, the knowledge points related to Class objects have been analyzed, and the reflection technology will be combined with the knowledge points analysis of Class objects.

Understanding Reflection Technology

Reflection mechanism is that all attributes and methods of this class can be known for any class in running state, and any method and attributes can be invoked for any object. The dynamic information obtained and the function of dynamic invoking methods of objects are called reflection mechanism of Java language. Reflection technology has always been a bright spot in Java, which is also the backbone of most frameworks (such as Spring/Mybatis, etc.) being implemented. In Java, Class class together with java. lang. reflection class library supports reflection technology. In reflection packages, we usually use Constructor classes to construct classes represented by Class objects. By using Constructor classes, we can dynamically create objects at runtime and Field classes represented by Class objects. By using Constructor classes, we can dynamically modify the attribute values of member variables (including private) and Method classes represented by Class objects at runtime. Method, through which the method of the object can be dynamically invoked (including private), these important classes will be explained separately below.

Constructor class and its usage

The Constructor class exists in the reflection package (java.lang.reflect), which reflects the construction method of the class represented by the Class object. Getting Constructor objects is obtained by methods in Class classes. The main methods related to Constructor in Class classes are as follows:

Method return value Method name Method description
static Class<?> forName(String className) Returns a Class object associated with a class or interface with a given string name.
Constructor<T> getConstructor(Class<?>... parameterTypes) Returns a constructor object of the specified parameter type with public access rights
Constructor<?>[] getConstructors() Returns an array of Constructor objects for all constructors with public access rights
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Returns the specified parameter type, all declared (including private) constructor objects
Constructor<?>[] getDeclaredConstructor() Returns all declared (including private) constructor objects
T newInstance() Create a new instance of the class represented by this Class object.

Let's look at a simple example to understand the use of Constructor objects:

package reflect;

import java.io.Serializable;
import java.lang.reflect.Constructor;

/**
 * Created by zejian on 2017/5/1.
 * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original]
 */
public class ReflectDemo implements Serializable{
    public static void main(String[] args) throws Exception {

        Class<?> clazz = null;

        //Get a reference to the Class object
        clazz = Class.forName("reflect.User");

        //First, instantiate the default constructor. User must have a parametric constructor, or it will throw an exception.
        User user = (User) clazz.newInstance();
        user.setAge(20);
        user.setName("Rollen");
        System.out.println(user);

        System.out.println("--------------------------------------------");

        //Get the public constructor with String parameters
        Constructor cs1 =clazz.getConstructor(String.class);
        //Create User
        User user1= (User) cs1.newInstance("xiaolong");
        user1.setAge(22);
        System.out.println("user1:"+user1.toString());

        System.out.println("--------------------------------------------");

        //Gets the specified constructor with int and String parameters, which is a private constructor
        Constructor cs2=clazz.getDeclaredConstructor(int.class,String.class);
        //Accessibility must be set because it is private
        cs2.setAccessible(true);
        //Create user objects
        User user2= (User) cs2.newInstance(25,"lidakang");
        System.out.println("user2:"+user2.toString());

        System.out.println("--------------------------------------------");

        //Get all constructs containing private
        Constructor<?> cons[] = clazz.getDeclaredConstructors();
        // View the parameters required for each constructor
        for (int i = 0; i < cons.length; i++) {
            //Get the constructor parameter type
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.println("Constructor["+i+"]:"+cons[i].toString() );
            System.out.print("Parameter type["+i+"]:(");
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            }
            System.out.println(")");
        }
    }
}


class User {
    private int age;
    private String name;
    public User() {
        super();
    }
    public User(String name) {
        super();
        this.name = name;
    }

    /**
     * Private structure
     * @param age
     * @param name
     */
    private User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

  //Omit set and get methods
}

Operation results:

User [age=20, name=Rollen]
--------------------------------------------
user1:User [age=22, name=xiaolong]
--------------------------------------------
user2:User [age=25, name=lidakang]
--------------------------------------------
Constructor [0]: private reflection. User (int, java. lang. String)
Parameter type [0]:(int,java.lang.String)
Constructor [1]:public reflect.User(java.lang.String)
Parameter type [1]:(java.lang.String)
Constructor [2]:public reflect.User()
Parameter type [2]: ()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Some common methods for the Constructor class itself are as follows (only part, other APIs are available).

Method return value Method name Method description
Class<T> getDeclaringClass() Returns a Class object, which represents a class that declares the constructor represented by the Constructor object, in fact returns the true type (without parameters).
Type[] getGenericParameterTypes() Returns a set of Type objects in declarative order, returning the parameter type of the Constructor object constructor.
String getName() Returns the name of this constructor as a string.
Class<?>[] getParameterTypes() Returns a set of Class objects in declarative order, that is, the parameter type of the constructor represented by the Constructor object
T newInstance(Object... initargs) Use the constructor represented by this Constructor object to create a new instance
String toGenericString() Returns a string describing this Constructor, including type parameters.

The code is illustrated as follows:

Constructor cs3=clazz.getDeclaredConstructor(int.class,String.class);

System.out.println("-----getDeclaringClass-----");
Class uclazz=cs3.getDeclaringClass();
//Classes of Constructor Object Representation Construction Methods
System.out.println("Classes of Construction Methods:"+uclazz.getName());

System.out.println("-----getGenericParameterTypes-----");
//Object represents the parameter type of the method represented by this Constructor object
Type[] tps=cs3.getGenericParameterTypes();
for (Type tp:tps) {
    System.out.println("Parameter name tp:"+tp);
}
System.out.println("-----getParameterTypes-----");
//Get the constructor parameter type
Class<?> clazzs[] = cs3.getParameterTypes();
for (Class claz:clazzs) {
    System.out.println("Parameter name:"+claz.getName());
}
System.out.println("-----getName-----");
//Returns the name of this constructor as a string
System.out.println("getName:"+cs3.getName());

System.out.println("-----getoGenericString-----");
//Returns a string describing this Constructor, including type parameters.
System.out.println("getoGenericString():"+cs3.toGenericString());
/**
 Output results:
 -----getDeclaringClass-----
 Class of constructor: reflect.User
 -----getGenericParameterTypes-----
 Parameter name tp:int
 Parameter name tp:class java.lang.String
 -----getParameterTypes-----
 Parameter name: int
 Name of parameter: java.lang.String
 -----getName-----
 getName:reflect.User
 -----getoGenericString-----
 getoGenericString():private reflect.User(int,java.lang.String)
 */

Here's a brief description of the Type type, which is a common high-level interface of all types in the Java programming language. They include primitive types, parameterized types, array types, type variables, and basic types. getGenericParameterTypes and getParameterTypes are both parameter types to obtain the function. The former returns the Type type, and the latter returns the Class type. Because of the top-level interface of Type, Class implements the interface, so Class class is a subclass of Type. Type represents all types and each Class object represents an instance of a specific type, such as String.class only represents String. Ng type. From this point of view, Type and Class represent almost the same type, except that the scope of Type is much wider than Class. Of course, Type has other subclasses, such as:

  • TypeVariable: Represents type parameters with upper bounds, such as T extends Number

  • ParameterizedType: Represents parameterized types, with primitive types and specific type parameters, such as List < String >

  • Wildcard Type: Represents wildcard types, such as:?,? Extends Number,? Super Integer

Through the above analysis, we have a clear understanding of Constructor class. By making good use of Class class and Constructor class, we can create arbitrary objects dynamically at runtime, thus breaking through the obstacle that we must know the exact type at compile time.

Field class and its usage

Field provides information about a single field of a class or interface and dynamic access to it. The reflected field may be a class (static) field or an instance field. In the same way, we can obtain field objects representing field information through the method provided by Class class. Class class and field object related methods are as follows:

Method return value Method name Method description
Field getDeclaredField(String name) Gets the field with the specified name (including the private modifier), excluding inherited fields
Field[] getDeclaredField() Gets all (including private ly modified) fields of the class or interface represented by the Class object, excluding inherited fields
Field getField(String name) Gets a field with a specified name and a public modifier, including inheritance fields
Field[] getField() Gets a field with a public modifier, including inherited fields

 
The following code demonstrates the use of the above method

/**
 * Created by zejian on 2017/5/1.
 * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original]
 */
public class ReflectField {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> clazz = Class.forName("reflect.Student");
        //Gets the Field class with the specified field name, noting that the field modifier must be public and that the field exists.
        // Otherwise, throw NoSuchField Exception
        Field field = clazz.getField("age");
        System.out.println("field:"+field);

        //Get all fields with the modifier public, including parent fields. Note that only when the modifier public
        Field fields[] = clazz.getFields();
        for (Field f:fields) {
            System.out.println("f:"+f.getDeclaringClass());
        }

        System.out.println("================getDeclaredFields====================");
        //Get the fields of the current class (including private fields), and note that the fields of the parent class are not included.
        Field fields2[] = clazz.getDeclaredFields();
        for (Field f:fields2) {
            System.out.println("f2:"+f.getDeclaringClass());
        }
        //Gets the Field class of the specified field name, which can be automated by any modifier. Note that fields that do not contain the parent class
        Field field2 = clazz.getDeclaredField("desc");
        System.out.println("field2:"+field2);
    }
    /**
      Output results: 
     field:public int reflect.Person.age
     f:public java.lang.String reflect.Student.desc
     f:public int reflect.Person.age
     f:public java.lang.String reflect.Person.name

     ================getDeclaredFields====================
     f2:public java.lang.String reflect.Student.desc
     f2:private int reflect.Student.score
     field2:public java.lang.String reflect.Student.desc
     */
}

class Person{
    public int age;
    public String name;
    //Omitting set and get methods
}

class Student extends Person{
    public String desc;
    private int score;
    //Omitting set and get methods
}

It should be noted that if we do not expect to get the fields of its parent class, we need to use the getDeclaredField/getDeclaredFields method of the Class class to get the fields. If we need to get the fields of the parent class jointly and jointly, we can use the getField/getFields of the Class class, but we can only get the fields decorated by public, and we can not get the private fields of the parent class. The following code demonstrates how to assign the specified class attributes through the Field class itself.

//Getting Class Object References
Class<?> clazz = Class.forName("reflect.Student");

Student st= (Student) clazz.newInstance();
//Get the parent public field and assign values
Field ageField = clazz.getField("age");
ageField.set(st,18);
Field nameField = clazz.getField("name");
nameField.set(st,"Lily");

//Get only the fields of the current class, not the fields of the parent class
Field descField = clazz.getDeclaredField("desc");
descField.set(st,"I am student");
Field scoreField = clazz.getDeclaredField("score");
//Set accessibility, score is private
scoreField.setAccessible(true);
scoreField.set(st,88);
System.out.println(st.toString());

//Output: Student {age = 18, name ='Lily, desc ='I am student', score = 88} 

//Get field values
System.out.println(scoreField.get(st));
// 88

The set(Object obj, Object value) method is the method of the Field class itself, which is used to set the value of the field, while the get(Object obj) is used to get the value of the field. Of course, there are other commonly used methods about the Field class as follows:

Method return value Method name Method description
void set(Object obj, Object value) Sets the field represented by this Field object on the specified object variable to the specified new value.
Object get(Object obj) Returns the value of the Field represented by this Field on the specified object
Class<?> getType() Returns a Class object that identifies the declaration type of the field represented by this Field object.
boolean isEnumConstant() If this field represents an element of the enumeration type, it returns true; otherwise, it returns false.
String toGenericString() Returns a string describing the Field, including its general type
String getName() Returns the name of the field represented by this Field object
Class<?> getDeclaringClass() Returns a Class object representing a class or interface that declares the fields represented by this Field object
void setAccessible(boolean flag) Set the accessible flag of this object to the Boolean value indicated, that is, to set its accessibility

 
These methods may be more commonly used. In fact, the Field class also provides methods specifically for basic data types, such as setInt()/getInt(), setBoolean()/getBoolean, setChar()/getChar(), and so on. Not all of them are listed here. You can check the API documents when you need them. Special attention should be paid to the fact that the Field field modified by final keyword is secure and can accept any modification at run time, but its actual value will not change in the end.

Method class and its usage

Method provides information about a single method on a class or interface (and how to access that method), which may reflect class methods or instance methods (including Abstract methods). The following is the Class class's method for obtaining method object-related methods:

Method return value Method name Method description
Method getDeclaredMethod(String name, Class<?>... parameterTypes) Returns a Method object with a specified parameter that reflects the specified declared method of the class or interface represented by the Class object.
Method[] getDeclaredMethod() 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 excluding inherited methods.
Method getMethod(String name, Class<?>... parameterTypes) Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.
Method[] getMethods() Returns an array containing some Method objects that reflect the common member methods of the classes or interfaces represented by the Class object, including those declared by the class or interface and those inherited from the superclass and superinterface.

The above method is also demonstrated by a case study.

import java.lang.reflect.Method;

/**
 * Created by zejian on 2017/5/1.
 * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original]
 */
public class ReflectMethod  {


    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {

        Class clazz = Class.forName("reflect.Circle");

        //Get public's Method based on parameters, including methods inherited from the parent class
        Method method = clazz.getMethod("draw",int.class,String.class);

        System.out.println("method:"+method);

        //Get all public methods:
        Method[] methods =clazz.getMethods();
        for (Method m:methods){
            System.out.println("m::"+m);
        }

        System.out.println("=========================================");

        //The method to get the current class contains private, which cannot get the method inherited from the parent class.
        Method method1 = clazz.getDeclaredMethod("drawCircle");
        System.out.println("method1::"+method1);
        //All methods to get the current class contain private, which cannot get methods inherited from the parent class
        Method[] methods1=clazz.getDeclaredMethods();
        for (Method m:methods1){
            System.out.println("m1::"+m);
        }
    }

/**
     Output results:
     method:public void reflect.Shape.draw(int,java.lang.String)

     m::public int reflect.Circle.getAllCount()
     m::public void reflect.Shape.draw()
     m::public void reflect.Shape.draw(int,java.lang.String)
     m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
     m::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
     m::public final void java.lang.Object.wait() throws java.lang.InterruptedException
     m::public boolean java.lang.Object.equals(java.lang.Object)
     m::public java.lang.String java.lang.Object.toString()
     m::public native int java.lang.Object.hashCode()
     m::public final native java.lang.Class java.lang.Object.getClass()
     m::public final native void java.lang.Object.notify()
     m::public final native void java.lang.Object.notifyAll()

     =========================================
     method1::private void reflect.Circle.drawCircle()

     m1::public int reflect.Circle.getAllCount()
     m1::private void reflect.Circle.drawCircle()
     */
}

class Shape {
    public void draw(){
        System.out.println("draw");
    }

    public void draw(int count , String name){
        System.out.println("draw "+ name +",count="+count);
    }

}
class Circle extends Shape{

    private void drawCircle(){
        System.out.println("drawCircle");
    }
    public int getAllCount(){
        return 100;
    }
}

When the method object is acquired by getMethods method, the method of the parent class is also acquired, such as the output result above, and the method of the Object class is printed out. The getDeclaredMethod/getDeclaredMethods method can only get the method of the current class. When we use it, we can choose it according to the situation. Next, we will demonstrate how to invoke a method of a specified class through a Method object:

Class clazz = Class.forName("reflect.Circle");
//create object
Circle circle = (Circle) clazz.newInstance();

//Method Object Method for Getting Specified Parameters
Method method = clazz.getMethod("draw",int.class,String.class);

//Call through the invoke(Object obj,Object... args) method of the Method object
method.invoke(circle,15,"circles");

//Operations on private parametric methods
Method method1 = clazz.getDeclaredMethod("drawCircle");
//Modifying access identifiers for private methods
method1.setAccessible(true);
method1.invoke(circle);

//Operations on methods that are worth returning
Method method2 =clazz.getDeclaredMethod("getAllCount");
Integer count = (Integer) method2.invoke(circle);
System.out.println("count:"+count);

/**
    Output results:
    draw Circle, count=15
    drawCircle
    count:100
*/

The invoke(Object obj,Object... args) of the Method class is used to invoke the method in the above code. The first parameter represents the invoked object and the second parameter passes the parameters of the invoke method. This completes the dynamic invocation of class methods.

Method return value Method name Method description
Object invoke(Object obj, Object... args) Call the underlying method represented by the Method object for the specified object with the specified parameter.
Class<?> getReturnType() Returns a Class object that describes the formal return type of the method represented by the Method object, that is, the return type of the method.
Type getGenericReturnType() Returns a Type object representing the formal return type of the method represented by this Method object, which is also the return type of the method.
Class<?>[] getParameterTypes() Returns an array of Class objects in declarative order that describe the type of parameters of the method represented by the Method object. That is, an array of the parameter types of the return method
Type[] getGenericParameterTypes() Returns an array of Type objects in declarative order that describe the parameter type of the method represented by the Method object and also the parameter type of the return method.
String getName() Returns the method name represented by this Method object in String form, that is, the name of the method returned.
boolean isVarArgs() Determine whether the method has variable parameters, and return true if the method is declared to have variable numbers of parameters; otherwise, return false.
String toGenericString() Returns a string describing this Method, including type parameters.

 
The getReturnType method and getGenericReturnType method are all the return types of the methods that get the representation of the Method object, but the Class type returned by the former and the Type returned by the latter (analyzed earlier), which is just an interface. A default method implementation is added in Java 8 to return the parameter type information.

public interface Type {
    //1.8 NEW
    default String getTypeName() {
        return toString();
    }
}

The same is true for getParameterTypes/getGenericParameterTypes, which are all parameter types for methods represented by Method objects. Other methods are similar to the previous Field and Constructor.

Array Classes in Reflection Packets

In Java's java.lang.reflect package, there is a class that can dynamically manipulate arrays, Array, which provides a way to dynamically create and access Java arrays. Array allows values and assignments to be performed in get or set operations. The methods associated with arrays in Class classes are:

Method return value Method name Method description
Class<?> getComponentType() Returns a Class representing the element type of an array, that is, the type of an array.
boolean isArray() Determines whether this Class object represents an array class.

The common static methods in java.lang.reflect.Array are as follows:

Method return value Method name Method description
static Object set(Object array, int index) Returns the value of the index component in the specified array object.
static int getLength(Object array) Returns the length of the specified array object in int form
static object newInstance(Class<?> componentType, int... dimensions) Create a new array with the specified type and dimension.
static Object newInstance(Class<?> componentType, int length) Create a new array with the specified component type and length.
static void set(Object array, int index, Object value) Sets the value of the index component in the specified array object to the specified new value.

Here's a simple example to illustrate these approaches

package reflect;

import java.lang.reflect.Array;

/**
 * Created by zejian on 2017/5/1.
 * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original]
 */
public class ReflectArray {

    public static void main(String[] args) throws ClassNotFoundException {
        int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        //Get the Class of the array type, which is int.class.
        Class<?> clazz = array.getClass().getComponentType();
        //Create a new array with the specified component type and length.
        //The first parameter is the type of the array, and the second parameter is the length of the array.
        Object newArr = Array.newInstance(clazz, 15);
        //Get the length of the original array
        int co = Array.getLength(array);
        //Assignment of primitive arrays to new arrays
        System.arraycopy(array, 0, newArr, 0, co);
        for (int i:(int[]) newArr) {
            System.out.print(i+",");
        }

        //Create an array of strings of 10 lengths.
        //Then set the element with index position 6 to "hello world!" and read the value of the element with index position 6.
        Class clazz2 = Class.forName("java.lang.String");

        //Create an array of strings 10 in length, which can also be used as an Object object in Java
        Object array2 = Array.newInstance(clazz2, 10);

        //Set the element whose index position of the string array object is 6 to "hello"
        Array.set(array2, 6, "hello world!");

        //Gets the value of the element whose index position is 5 for the string array object
        String str = (String)Array.get(array2, 6);
        System.out.println();
        System.out.println(str);//hello
    }
    /**
     Output results:
     1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
     hello world!
     */
}

Through the above code demonstration, we can use Array class and reflection to create arrays dynamically. We can also dynamically get and set the values of elements in arrays at run time. In fact, besides the set/get above, Array also provides special methods for eight basic data types, such as setInt/getInt, setBoolean/getBoolean, and so on. Yes. In addition to the above dynamic modification of array length or dynamic creation of arrays or dynamic acquisition or setting of values, generic arrays can be dynamically created using generics as follows:

/**
  * Receive a generic array, and then create a generic array of the same length as the received array.
  * And copy the elements of the received array into the newly created array.
  * Find out the smallest element in the new array and print it out.
  * @param a
  * @param <T>
  */
 public  <T extends Comparable<T>> void min(T[] a) {
     //Create the same type of array by reflection
     T[] b = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length);
     for (int i = 0; i < a.length; i++) {
         b[i] = a[i];
     }
     T min = null;
     boolean flag = true;
     for (int i = 0; i < b.length; i++) {
         if (flag) {
             min = b[i];
             flag = false;
         }
         if (b[i].compareTo(min) < 0) {
             min = b[i];
         }
     }
     System.out.println(min);
 }

After all, we can't create generic arrays directly. With Array's way of creating arrays dynamically, the problem can be solved.

//Invalid statement, not compiled properly
T[] a = new T[];

ok ~, we have basically introduced several important and commonly used classes of this reflection, but more importantly, we should realize that there is nothing magical about the reflection mechanism. When dealing with an object of unknown type by reflection, the JVM simply checks the object to determine which type it belongs to. At the same time, it should know that before creating an object with reflection mechanism, it must ensure that the Class object of this class has been loaded. Of course, this need not be operated by us at all. After all, it can only be loaded by JVM, but it must ensure that the class of this class. ” The file already exists and the JVM can find it correctly. In fact, there are a lot of API methods for Class classes. It is recommended to check the API documentation and browse through it. It is also a good choice to have an impression. Here is only a list of APIs that have not been introduced before and may be used.

 /** 
  *    Modifiers, parent classes, implementation interfaces, annotations 
  */

//Get the modifier, and the return value can be interpreted through the Modifier class
public native int getModifiers();
//Get the parent class, null if it is Object
public native Class<? super T> getSuperclass();
//For classes, all interfaces declared for themselves, for interfaces, directly extended interfaces, excluding indirect inheritance through parent classes
public native Class<?>[] getInterfaces();
//Annotations to self-statements
public Annotation[] getDeclaredAnnotations();
//All annotations, including inherited ones
public Annotation[] getAnnotations();
//Gets or checks annotations of a specified type, including inherited ones
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);

/** 
  *   Internal class correlation
  */
//Get all public internal classes and interfaces, including those inherited from the parent class
public Class<?>[] getClasses();
//Get all the internal classes and interfaces you declare
public Class<?>[] getDeclaredClasses();
//If the current Class is an internal class, get the outermost Classs object that declares the class
public Class<?> getDeclaringClass();
//If the current Class is an internal class, get the class that contains the class directly.
public Class<?> getEnclosingClass();
//If the current Class is a local class or an anonymous internal class, return the method that contains it
public Method getEnclosingMethod();

/** 
  *    Class Object Type Judgment Relevance
  */
//Is it an array?
public native boolean isArray();  
//Is it a basic type?
public native boolean isPrimitive();
//Is it an interface?
public native boolean isInterface();
//Is it an enumeration?
public boolean isEnum();
//Is it a commentary?
public boolean isAnnotation();
//Is it an anonymous inner class?
public boolean isAnonymousClass();
//Is it a member class?
public boolean isMemberClass();
//Are they local?
public boolean isLocalClass(); 

ok, that's the end of this article.

Keywords: Java jvm JDK Unity

Added by Goafer on Fri, 24 May 2019 00:51:07 +0300