Reflection is an operation that dynamically retrieves the type information of a class at runtime. It is the soul of modern frameworks, and as much automation as any framework can provide relies on reflection, which is why frameworks don’t allow you to override the default no-argument constructor, because frameworks need reflection to create instances using no-argument constructors.
Overall, reflection is well worth the time to learn, and although most people rarely have the opportunity to write frames by hand, it will help you understand the various types of frames. I don’t expect you to have a deep understanding of reflection through this article, but at least you will understand the basic principles and uses of reflection.
Class type information
In this article, we mentioned that each type is loaded into the “method area” of the VIRTUAL machine memory when it is first used, which contains the property fields defined in the class, method bytecode, and other information.
Java uses the java.lang.Class Class to point to a type of information. From this Class object, we can get all the internal information about that Class. There are three main ways to get a Class object.
The name of the class. The class
In this way, it is easier to use the class name point class to get the type information of the type in the method area. Such as:
Object.class; Integer.class; int.class; String.class; / / etc.Copy the code
GetClass method
The Object class has this method:
public final native Class<? > getClass();Copy the code
This is a native method and does not allow subclass overrides, so in theory all instances of the type have the same getClass method. Specific use is also very simple:
Integer integer = new Integer(12);
integer.getClass();
Copy the code
Class.forname method
ForName is one of the most common methods for getting a Class type. It allows you to pass in a full Class name. This method returns the Class object representing the type in the method area.
public static Class<? >forName(String className) { Class<? >caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
Copy the code
Since the method area Class type information is uniquely determined by the Class loader and the Class fully qualified name, finding such a Class must provide both the Class loader and the Class fully qualified name. The forName method defaults to using the caller’s Class loader.
Of course, there is also a forName overload in the Class Class that allows you to pass in the Class loader and the Class fully qualified name to match the method area type information.
public static Class<? >forName(String name, boolean initialize,
ClassLoader loader){
//.....
}
Copy the code
At this point, you can use these methods to get the type information of any Class, all the field properties of that Class, the method table and so on can be retrieved from this Class object.
Reflection field properties
The following methods are used to obtain field attributes in Class:
- Public Field[] getFields() : Returns all public-modified attributes of the type, including those of the parent class
- Public Field getField(String name) : Returns the corresponding Field according to the Field name
- Public Field[] getDeclaredFields() : returns all fields declared in this type, including non-public fields but not in the parent class
- Public Field getDeclaredField(String name) : Similarly
Of course, a Field instance contains all the information about an attribute of a class, including the Field name, access modifiers, and Field type. In addition, Field provides a number of methods for manipulating the value of that attribute. By passing in a class instance, you can directly use the Field instance to manipulate the value of that instance’s current Field attribute.
Such as:
Public class People {public String name; }Copy the code
Class<People> cls = People.class;
Field name = cls.getField("name");
People people = new People();
name.set(people,"hello");
System.out.println(people.name);
Copy the code
The program prints:
hello
Copy the code
The set method retrieves whether the People object has a field represented by name, if it assigns the string Hello to that field.
The whole Field Class is mainly composed of two parts. The first part is the description information about the Field attributes, such as name, type, and peripheral Class object, etc. The second part is a large number of GET and set methods used to indirectly manipulate the current attribute value of any peripheral Class instance.
The reflection method
Similarly, the Class Class provides four methods for retrieving method attributes:
- Public Method[] getMethods() : Returns all public methods, including those in the parent class
- public Method getMethod(String name, Class
… ParameterTypes) : returns the specified method - Public Method[] getDeclaredMethods() : Returns all methods declared by this class, including non-public ones, but not including those in the parent class
- public Method getDeclaredMethod(String name, Class
… ParameterTypes) : in the same way
Method abstractly represents a Method. It also has fields and methods that describe the basic information of the Method, such as Method name, Method parameter set, Method return value type, exception type set, Method annotation, etc.
In addition, there is an invoke method that indirectly calls the method of another instance, for example:
public class People {
public void sayHello(){
System.out.println("hello wrold "); }}Copy the code
Class<People> cls = People.class;
Method sayHello = cls.getMethod("sayHello");
People people = new People();
sayHello.invoke(people);
Copy the code
Program output:
hello wrold
Copy the code
Reflection constructor
For Constructor, the Class Class still provides it with four methods of getting instances:
- public Constructor
[] getConstructors() : Returns all public decorated constructors - public Constructor
[] getDeclaredConstructors() : returns all constructors, regardless of the access modifier - public Constructor getConstructor(Class
… ParameterTypes: with specified parameters - public Constructor getDeclaredConstructor(Class
… ParameterTypes) : in the same way
Constructor is essentially a Method that returns no value, so the underlying content is similar to Method, except that the Constructor Class uses a newInstance Method to create an instance object of the Class type.
Class<People> CLS = people.class; Constructor c = cls.getConstructor(); People p = (People) c.newInstance();Copy the code
Above, we’ve covered the basics of reflection, but it’s very basic. Let’s look at reflection in combination with slightly more complex types such as arrays, generics, annotations, and so on.
Other details of reflection
Reflection and Array
As we all know, arrays are a special type that is essentially dynamically generated by the virtual machine at runtime, so reflecting this type will be slightly different.
public native Class<? > getComponentType();Copy the code
Class has a method that returns the primitive type of an element of an array Class instance. This method returns the actual element type of the array only if the current Class object represents an array type; otherwise, it returns NULL.
It is important to note, of course, that the virtual-created type representing arrays directly inherits from the Object class, and that all operations on the array class, such as assigning an element or obtaining an array length, correspond directly to a single virtual array instruction.
Also, because array classes are created dynamically directly from the virtual machine at runtime, you cannot get a constructor from an array Class instance, and the compiler has no chance of generating a default constructor for the Class. Thus, you cannot create an instance object of this class using Constructor in the normal way.
If you must try to create a new instance using Constructor, the runtime will tell you that a Constructor cannot be matched. Like this:
Class<String[]> cls = String[].class;
Constructor constructor = cls.getConstructor();
String[] strs = (String[]) constructor.newInstance();
Copy the code
Console output:
For your information, you can’t find a constructor with no arguments in the Class instance. Isn’t there any way to create an array dynamically?
Of course not. There is a class in Java called java.lang.Reflect. Array that provides static methods for dynamically creating and retrieving an Array type.
Public static Object newInstance(Class<? > componentType, int length) // Dimensions, specifying a single dimension length public static Object newInstance(Class<? > componentType, int... dimensions)Copy the code
These are the two most important methods in the Array class in my opinion, although there are other methods in the Array class that get the elements of a given Array at a given location, which I won’t go into here.
Because arrays are not compiled by a regular compiler, they are created by the virtual machine on the fly, so instantiating an Array type by reflection depends on the newInstance method of Array.
Reflection and generics
Generics are a concept within the scope of the Java compiler to provide some kind of security check before the program runs, whereas reflection happens at run time, meaning that if you call a generic method by reflection, you actually bypass the compiler’s generics check. Let’s look at some code:
ArrayList<Integer> list = new ArrayList<>();
list.add(23);
//list.add("fads"); Does not compile through Class<? > cls = list.getClass(); Method add = cls.getMethod("add",Object.class);
add.invoke(list,"hello");
System.out.println(list.get(1));
Copy the code
Eventually you’ll find that we’re pulling a string out of the int container, because the virtual machine just finds the type information for the ArrayList class from the method area at run time and parses its Add method, and then executes it.
Unlike normal method calls, where the compiler checks whether the method exists, parameter types match, and so on, without the compiler’s layer of security, calling a method reflectively is more likely to run into problems.
In addition, we mentioned earlier that generics are erased by type after compile time, but there is actually some basic generic information stored in the Class type information representing the type, which we can see by reflection.
Class, Field, and Method are all related methods that retrieve the generic Class name used by the Class or Method when it is defined. Notice it’s just names, E, V, things like that.
All the code, images and files in this article are stored in the cloud on my GitHub:
(https://github.com/SingleYam/overview_java)
Welcome to follow the wechat official account: OneJavaCoder. All articles will be synchronized on the official account.