The introduction

How many ways can you create objects in Java?

1. Use the new keyword

2. Use the Clone method

Use deserialization

4. Use reflection

5. Use the Unsafe

For a detailed explanation of these methods, see this article five Ways to Create objects in Java

Next, reflection is introduced in detail

Reflection profile

Reflection contains the word “negative”, so to explain reflection one must begin with “positive”. In general, when we use a class, we must know what it is and what it does. We instantiate the class directly and then operate with the class object.

Apple apple = new Apple(); // Direct initialization, "ortho"
apple.setPrice(4);
Copy the code

The above initialization of the class object can be understood as “positive”. Reflection, on the other hand, doesn’t know what class object I’m initializing, so I can’t use the new keyword to create the object. In this case, we use the reflection API provided by the JDK to make reflection calls:

Class clz = Class.forName("com.eft.reflect.Apple");
Method method = clz.getMethod("setPrice".int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
Copy the code

The result of executing the above two pieces of code is exactly the same. But the idea is quite different. The first code already knows which class to run (Apple) before it runs (compile time), while the second code knows which class to run (com.eft.reflect.apple) from the string value at run time.

So what is reflection?

Reflection is when you know what class to operate on at run time, and you can get the complete construct of the class at run time and call the corresponding method.

The official definition of

The JAVA reflection mechanism allows you to know all the properties and methods of any class in the running state. For any object, you can call any of its methods and properties; This ability to dynamically retrieve information and invoke methods on objects is called the Reflection mechanism of the Java language.

An important part of the reflection mechanism is the “runtime”, which allows us to load, explore, and use.class files that are completely unknown at compile time while the program is running. In other words, a Java program can load a.class file whose name is not known until runtime, then learn of its complete construction and generate its object entities, or set values for its fields, or call its methods.

Popular generalization

Reflection is a technique that allows you to get information about an object (such as classes, properties, methods, etc.) from its name

The core

The Java reflection mechanism is a key property of the Java language that is considered a “quasi-dynamic” language. At the heart of the Java Reflection mechanism is the ability to use the Java Reflection APIs at runtime to obtain internal information about classes with known names (including their modifiers(such as public, Static, etc.), superclass(e.g. Object), implement interfaces(e.g. Serializable), and also all information about fields and methods, dynamically generate this class, And call its methods or modify its fields (even fields or methods that are themselves declared private)

function

  • Determine the class of any object at run time;
  • Construct an object of any class at runtime;
  • Determine which member variables and methods any class has at run time.
  • Call a method of any object at runtime;
  • Generating dynamic proxies

Reflection principle

The flow of class loading:











To use reflection, you first need to get the Class object corresponding to the Class to be operated on. In Java, no matter how many objects of a Class are generated, they all correspond to the same Class object. This Class object is generated by the JVM to give you an idea of the structure of the entire Class. So, java.lang.Class can be seen as the entry point for all reflection apis. The essence of reflection is to map components of a Java class to Individual Java objects at run time.

A simple example

From the previous introduction – using reflection to create an object example, we saw how to create an object using reflection:

Gets the Class object instance of the Class

Class clz = Class.forName("com.eft.reflect.Person");
Copy the code

Get the Constructor object from the Class object instance

Constructor constructor = clz.getConstructor();
Copy the code

Get the reflection class object using the newInstance method of the Constructor object

Object personObj = constructor.newInstance();
Copy the code

If you want to call a method, you need to do the following:

  • Gets the Method object of the Method
Method setNameMethod = clz.getMethod("setName", String.class);
Copy the code
  • Invoke a method using the Invoke method
setNameMethod.invoke(personObj, Hot and Sour soup);
Copy the code

Test code for calling a method through reflection:

Class clz = Person.class;
Method setNameMethod = clz.getMethod("setName", String.class);
// Person person= (Person) clz.getConstructor().newInstance();
Person person= (Person) clz.newInstance();
setNameMethod.invoke(person, "Hot and Sour Soup 888");// Call the setName method, passing in the argument

Method getNameMethod = clz.getMethod("getName".null);
String name= (String) getNameMethod.invoke(person,null);// Call getName to get the return value

System.out.println("name:"+name); Running result: name: hot and sour soup888
Copy the code

At this point, we have been able to grasp the basic use of reflection. But to get a better grasp of reflection, you need to have a deeper understanding of the common apis for reflection

Reflection API details

In the JDK, reflection related apis fall into three categories:

Get the reflection Class object

Each type (e.g. String,Integer,Person…) They are loaded into the “method area” of the virtual machine’s memory for the first time and contain information such as property fields defined in the class, method bytecodes, and so on. 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.

Class has no public constructor. Class objects are automatically constructed by the Java Virtual machine when the Class is loaded and by calling the defineClass method in the Class loader.

There are three main ways to get a Class object:

Use class literal constants or TYPE fields

  • Class, such as Person.class
    • Class literal constants can be applied not only to ordinary classes, but also to interfaces, arrays, and primitive data types
    • This approach is not only simpler, but also more secure, because it is checked at compile time and eliminates calls to the forName method, so it is also more efficient. The “.class “form is recommended
  • Boolean TYPE, such as an Integer TYPE
    • TYPE is a standard field for the wrapper TYPE of the base datatype, which is a reference to the corresponding Class object of the base datatype

Both sides of the table are equivalent:

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

This method is the most direct, but it can only obtain the Class object of the Class I know, that is, the object of the Class used in the project can obtain its Class object through the class.class method, but this method has a disadvantage is that the unknown Class, or invisible Class can not obtain its Class object.

Object. GetClass ()

Such as: Person.getclass () The ancestor of Java’s Object Class provides a method called getClass() to get an instance’s Class Object. This method is the most commonly used method in development. It also cannot get unknown classes, such as the Class Object of an interface implementation.

API:

public final nativeClass<? > getClass();Copy the code

This is a native Method (a Native Method is a Java interface that calls non-Java code) and does not allow subclass overrides, so in theory all instances of a type should have the same getClass Method.

Use:

Integer integer = new Integer(12);
Class clz=integer.getClass();
Copy the code

Class.forname (” Class full path “)

For example, class.forname (” com.eft.xx.person “) is the most commonly used method to obtain the Class object of any Class, provided that the Class exists, otherwise throwing a ClassNotFoundException will be thrown. In this way, we only need to know the Class’s full path (fully qualified name) to get its Class object (if it exists).

API:

// Since the method area Class type information is uniquely determined by the classloader and the Class fully qualified name,
// To find such a Class, you must provide the classloader and the fully qualified name of the Class.
// The forName overloaded method allows you to pass in the class loader and the class fully qualified name to match the method area type information
// Name :class name, initialize whether to load the static block
public staticClass<? > forName(String name,boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException{
    ..
    }   

// This forName method defaults to using the caller's classloader to load the class's.class file into the JVM
// We pass initialize to true, which executes the static block of the class
public staticClass<? > forName(String className)throws ClassNotFoundException {
    return forName0(className, true, ClassLoader.getCallerClassLoader());
}
Copy the code

Use:

Class clz = Class.forName("com.eft.reflect.Person");
Copy the code

2. Determine whether it is an instance of a class

  • Use the instanceof keyword
  • Using the isInstance method of a Class object (Native method)
public class InstanceofDemo {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        if (arrayList instanceof List) {
            System.out.println("ArrayList is List");
        }
        if (List.class.isInstance(arrayList)) {
            System.out.println("ArrayList is List"); }}}//Output:
//ArrayList is List
//ArrayList is List
Copy the code

Classes loaded by different loaders do not belong to the same category (the instant package name and class name are the same), and the created objects also belong to different classes, as follows:

ClassLoader myLoader = new ClassLoader() {
            @Override
            publicClass<? > loadClass(String name)throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream("./bean/" + fileName);
                    if (is == null) {
                        return super.loadClass(name);// Returns the parent class loader
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (Exception e) {
                    throw newClassNotFoundException(); }}}; Object obj =null;
Class clz=myLoader.loadClass("eft.reflex.bean.Person");
System.out.println("Person is loaded by the custom class loader.");
obj = clz.newInstance();
System.out.println(obj instanceofPerson); Result: The person is loaded by the custom classloader and the static block of person is calledfalse
Copy the code

The JVM will use the default class loader to load the class into the method area based on the operator to the right of instanceof, and then look for the corresponding class in the method area based on the class reference address information in the object header of the operator object on the left. The result is true if the class found is the newly loaded class, and false otherwise. For this example, obj, pointing to the object class in creating objects before loading to the method, after undergoing the instanceof due to the already existing in the method of area for the time of the class is not using the default loader to load, therefore the JVM think this class is not loaded, so the right side of the operator, pointing to the class to be loaded at this time, So the result of this example is false. If you change the class name in parentheses to the class name of the test class, the result is similar, except that the test class is loaded before the main method executes.

Get the constructor by reflection and create the instance object

There are two main ways to create Class objects through reflection: through the newInstance() method of a Class object and through the newInstance() method of a Constructor object.

The first is through newInstance() on Class objects.

  • public T newInstance()
    • Requires that the constructor being called be visible, IllegalAccessException XXX can not access a member of class eft.reflex.Singleton with modifiers “private
    • You can only call the constructor without arguments, which is the default constructor

The following apis are involved in this operation:

  • public Constructor<? >[] getConstructors() // Get all the visible constructors of the class object
  • public Constructor<? >[] getDeclaredConstructors()// Get all the constructors of the class object

Note: 1. GetConstructors and getDeclaredConstructors get an array of unordered constructors, so do not use the index to get the specified constructors. Methods with Declared will not return parent members but will return private members. Methods that are not Declared are the exact opposite of the following similar methods

  • public Constructor getConstructor(Class<? >… parameterTypes)
    • Gets the specified visible constructor as an array of the specified constructor’s argument types
    • If the constructor is not visible or does not exist, a NoSuchMethodException is thrown

Examples:

Class p = Person.class;
Constructor constructor1 = p.getConstructor();// Get the constructor without any arguments
Constructor constructor = p.getConstructor(String.class,int.class);Get the Person(String name,int age) constructor
Copy the code
  • public Constructor getDeclaredConstructor(Class<? >… parameterTypes)
    • Gets the specified constructor as an array of the specified constructor argument types
    • Is available regardless of constructor visibility

Examples:

Class p = Person.class;
Constructor constructor = p.getDeclaredConstructor(String.class,int.class);Get the Person(String name,int age) constructor
Copy the code
  • Constructor’s setAccessible and newInstance methods
// Turn off access checking, which must be set to true before reflection can access invisible constructors
// However, the compiler does not allow normal code to use this field, since it only applies to reflection
public void setAccessible(boolean flag) 

// Create an object with arguments of variable length, but you must provide an exact parameter for each argument when calling the constructor.
public T newInstance(Object ... initargs)Examples:// Suppose Person has a private Person(String name){} constructor
Constructor constructor = Person.class.getConstructor(String.class);
constructor.setAccessible(true);
Person person = (Person)constructor.newInstance(Hot and Sour soup);
Copy the code

For a full example of creating an object using Constructor: see Creating an Object using Reflection – Calling a class object Constructor above

4. Get class attributes and methods through reflection

You can use reflection to get a list of properties and methods of a Class object

The name of the class

  • Public String getName() public String getName() public String getName()
  • Public String getCanonicalName()// Get the class full pathname (returns a more understandable representation)
  • Public String getSimpleName() // Gets the class name without the package name

So what’s the difference between these three? Here’s a common chestnut class name:

Class clz=Person.class; System.out.println(clz); System.out.println(clz.toString()); System.out.println(clz.getName()); System.out.println(clz.getCanonicalName()); System.out.println(clz.getSimpleName()); Running results:class reflex.Person
class reflex.Person//ClassIt's rewrittentoStringMethod, and is called insidegetName() methodreflex.Person
reflex.Person
Person
Copy the code

Array:

Class clz=Person[][].class; System.out.println(clz.getName()); System.out.println(clz.getCanonicalName()); System.out.println(clz.getSimpleName()); Person[] Person[] Person[] Person[] Person[]Copy the code

The modifier

  • public native int getModifiers(); // Get the modifier

Modifiers are wrapped in an int, and each modifier is a flag bit (set or cleared). Can use Java. Lang. Reflect the Modifier in the class the following method to test the Modifier:

Modifier.isAbstract(int mod)
    Modifier.isFinal(int mod)
    Modifier.isInterface(int mod)
    Modifier.isNative(int mod)
    Modifier.isPrivate(int mod)
    Modifier.isProtected(int mod)
    Modifier.isPublic(int mod)v
    Modifier.isStatic(int mod)
    Modifier.isStrict(int mod)True if the mod contains strictfp (strict float point) modifier; Otherwise, the value is false.
    Modifier.isSynchronized(int mod)
    Modifier.isTransient(int mod)
    Modifier.isVolatile(int mod)
Copy the code

Examples:

Class clz= Person.class;
int modifier = clz.getModifiers();
System.out.println("Whether the modifier is public:"+ Modifier.isPublic(modifier)); Running results:true
Copy the code

Internal use of the Modifier & operation to judge, such as

public static boolean isPublic(int var0) {
    return (var0 & 1) != 0;
}
Copy the code

Package information

  • Public Package getPackage() // getPackage information

From the Package object you can access Package information such as the name. You can also access the information specified in the Manifest file in the JAR file on the classpath for the package. For example, you can specify the package version number in the Manifest file. You can learn more about Package classes in java.lang.Package.

The parent class

  • public native Class<? super T> getSuperclass(); // Get the immediate parent

The Class object of the parent Class is a Class object like any other Class object and can continue to use reflection

Implemented interface

  • public native Class
    [] getInterfaces(); // Get the list of implemented interfaces
  • A class can implement multiple interfaces. So return an array of classes. In Java reflection, interfaces are also represented by Class objects.
  • Note: Only the interface implemented by the given class declaration is returned. For example, if class A’s parent B implements an interface C, but class A does not declare that it also implements C, then C is not returned to the array. Even though class A actually implements interface C, because its parent class B implements C.

To get a complete list of the interfaces implemented by a given class, you need to recursively access the class and its superclasses

  • Public Type[] getGenericInterfaces() //getGenericInterface returns a Type that includes generics

field

  • Public Field[] getFields() public Field[] getFields() public Field[] getFields() public Field[] getFields(
  • Public Field[] getDeclaredFields(
  • Public Field getField(String name) // Get the character information from the Field name. The Field must be visible; otherwise, an exception is thrown
  • Public Field getDeclaredField(String name) // Obtain visible character information by Field name

On the Field
  • Public String getName() // getName
  • public Class<? > getType() // Get the type of a field

Examples:

Class clz = Person.class;
Field field = clz.getDeclaredField("name");

System.out.println("Get field name:" + field.getName());
System.out.println("Get field type:"+field.getType()); Field name: name Field type:class java.lang.String
Copy the code
  • Public Object get(Object obj) // Get the value of the field
  • Public void set(Object obj, Object value)

Note:

  1. If the obtained field is not visible, it must be made accessible using setAccessible(true) before being accessed via set and GET
  2. If it is a static field, obj passes NULL instead of a concrete object. However, it works if you pass a specific object

Examples:

Person person= (Person) clz.newInstance();
field.setAccessible(true);// Make it accessible
field.set(person, Hot and Sour soup);// Use the set method to set the field value
System.out.println("Value obtained by get:"+field.get(person)); Run result: get the value: hot and sour soupCopy the code

When reflection encounters a final modified field

Here’s an example:

public class FinalTest {

    public static void main(String[] args )throws Exception {
        Field nameField = OneCity.class.getDeclaredField("name");

        nameField.setAccessible(true);
        nameField.set(null."Shenzhen"); System.out.println(OneCity.getName()); }}class OneCity {
    private static final String name = new String("Beijing");

    public static String getName(a) {
        returnname; }}Copy the code

Output result:

Exception in thread "main" java.lang.IllegalAccessException: Can not set static final java.lang.String field eft.reflex.OneCity.name to java.lang.String
Copy the code

So how do I use reflection to change its value?

At this point we need to do a more thorough reflection – reflection of the classes in the Java reflection package. Field objects have a property called a modifiers that tells you if a property is a combination of public, private, static, and final modifier. This reflects this modifiers as well, removing the nameField final constraint and returning to the above situation. The complete code looks like this:

public class FinalTest {

    public static void main(String[] args )throws Exception {
        Field nameField = OneCity.class.getDeclaredField("name");

        Field modifiersField = Field.class.getDeclaredField("modifiers"); / / 1.
        modifiersField.setAccessible(true);
        modifiersField.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL); / / 2.

        nameField.setAccessible(true); // This can also be used unless private is removed
        nameField.set(null."Shenzhen");
        System.out.println(OneCity.getName()); / / output sought}}class OneCity {
    private static final String name = new String("Beijing");

    public static String getName(a) {
        returnname; }}Copy the code

Find the Field modifier at ①, it’s also a private variable, so setAccessible(ture) as well. Then change the nameField modifier (②) by bit-reversing the ~ and ~ modifier to remove final from the modifier set. Other features like private and static remain the same. Consider also that modifierField.setint () can change private to public, thus modifying name without the need for setAccessible(true).

By removing the final property, we successfully changed the name to Shenzhen.

Notice why OneCity’s name is assigned to new String(” Beijing “). This is to prevent the Java compiler from inlining name into the getName() method. Make getName() return “Beijing”, causing getName() to always print “Beijing”.

methods

  • Public Method[] getMethods() gets all visible methods
  • Public Method[] getDeclaredMethods() gets all methods, whether visible or not
  • public Method getMethod(String name, Class<? >… parameterTypes)
    • Get the method by method name and parameter type
    • If the method you want to access is not visible, an exception is thrown
    • If the method you want to access has no arguments, passnullAs an array of parameter types, or without passing values)
  • public Method getDeclaredMethod(String name, Class<? >… parameterTypes)
    • Get the method by method name and parameter type
    • If the method you want to access has no arguments, passnullAs an array of parameter types, or without passing values)

About the Method
  • public Class<? >[] getParameterTypes() // Gets all parameter types for the method
  • public Class<? > getReturnType() // Get the return value type of the method
  • public Object invoke(Object obj, Object… Args)// Call the method
    • Obj: the object that you want to call this method; Args: the specific parameters of a method, each of which must have an exact parameter
    • If the method is static, here obj passes null
    • If the method has no arguments, args passes null or none

Examples:

The Person class has a method like this:private void testMethod(String param){
    System.out.println("The testMethod method is called with:"+param);
}



// Call the method through reflection
Class clz = Person.class;
Method method=clz.getDeclaredMethod("testMethod",String.class);
method.setAccessible(true);
method.invoke(clz.newInstance(),"I am the specific parameter value."); Result: The testMethod method is called with the argument: I am the specific parameter valueCopy the code

About Method more API to view the source code

Java reflection lets you examine the methods of a class and invoke them at run time. This can be used to detect which GET and set methods a given class has. You can do this by scanning all the methods of a class and checking whether each method is a GET or set method. Here is the code to find the get and set methods of the class:

public static void printGettersSetters(Class aClass){

        Method[]methods = aClass.getMethods();

        for(Methodmethod : methods){
           if(isGetter(method))System.out.println("getter: " + method);
           if(isSetter(method))System.out.println("setter: "+ method); }}public staticboolean isGetter(Method method){
        if(! method.getName().startsWith("get"))      return false;
        if(method.getParameterTypes().length! =0)   return false; 
        if(void.class.equals(method.getReturnType())return false;
        return true;

}

public staticboolean isSetter(Method method){
    if(! method.getName().startsWith("set"))return false;
    if(method.getParameterTypes().length! =1) return false;
    return true;
}
Copy the code

annotations

  • Public Annotation[] getanannotations () // Retrieves all annotations of the current member, excluding inherited annotations; (since jdk1.5)
  • Public Annotation[] getDeclaredanannotations ()// Get any annotations including inheritance; (since jdk1.5)

More on the notes next time

Reflection and Array

Arrays: Define multiple variables of the same type As we all know, arrays are a special type that is essentially dynamically generated by the virtual machine at runtime, so they reflect this type slightly differently. 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

Throw a NoSuchMethodException that tells you that no constructor with no arguments can be found in the Class instance

So how do we dynamically create an array? The Java.lang.Reflect. Array class provides static methods for dynamically creating and retrieving an Array type

  • public static Object newInstance(Class<? > componentType, int length)
    • // Create a one-dimensional array. ComponentType is the element type of the array and Length is the length of the array
  • public static Object newInstance(Class<? > componentType, int… dimensions)
    • // dimensions: specifies the length of a single dimension for multiple dimensions
  • public static native void set(Object array, int index, Object value)
    • Set array index at index to value
  • public static native Object get(Object array, int index)
    • Gets the element at the index position of the array

Add the API for getting component types from Class:

  • public native Class<? > getComponentType();
    • Gets the type of its element if class is an array type, or null if it is not

One-dimensional array instance

// Use reflection to define an array of type int, 3 lengths
int[] intArray = (int[]) Array.newInstance(int.class, 3);

Array.set(intArray, 0.123);
Array.set(intArray, 1.456);
Array.set(intArray, 2.789);

System.out.println("intArray[0] = " + Array.get(intArray, 0));
System.out.println("intArray[1] = " + Array.get(intArray, 1));
System.out.println("intArray[2] = " + Array.get(intArray, 2));

// Get an array of class objects
Class stringArrayClass = Array.newInstance(int.class, 0).getClass();
System.out.println("is array: " + stringArrayClass.isArray());


// Get the component type of the array
String[] strings = new String[3]; Class stringArrayClass2 = strings.getClass(); Class stringArrayComponentType = stringArrayClass2.getComponentType(); System.out.println(stringArrayComponentType); Run result: intArray[0] = 123
intArray[1] = 456
intArray[2] = 789
is array: true
class java.lang.String
Copy the code

Multidimensional arrays:

Int [] dims = new int[] {5,10,15}; dims = new int[] {5,10,15}; Person[][][] array = (Person[][][]) Array.newInstance(Person.class, dims); Object array = array.newinstance (integer.type, 5,10,15); Class<? > classType0 = array.getClass().getComponentType(); Println (" 3d array element type :"+classType0); // Return the array element type system.out.println (" 3D array element type :"+classType0); ArrayObject = array.get (Array, 2); System.out.println(" arrayObject.getClass().getComponentType()) "); Object oneObject = Array.get(arrayObject, 0); System.out.println(" OneObject.getClass ().getComponentType() "); Array.set(oneObject,14,new Person(" sour ",18)); System.out.println(" unset element: "+array[0][0][0]); System.out.println(" array[2][0][14]); Running results: Person :class [Left. Reflex. Bean. Person :class [Left. Reflex Person{name=' hot and sour ', age=18}Copy the code

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"); Compile failedClass<? > cls = list.getClass(); Method add = cls.getMethod("add",Object.class);
add.invoke(list,"hello");
for(Object obj:list){ System.out.println(obj); } Run result:23
hello
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.

Use reflection to get generic information

In practice, in order to gain information about generics, Java has added several new types to represent types that cannot be generalized into a Class Class, but are named the same as primitive data types. The following two types are commonly used:

  • GenericType: Indicates that an element type is a parameterized type or an array type of type variables. @ since 1.5
  • ParameterizedType: indicates a ParameterizedType. @ since 1.5

In fact, the Field class has a method called getType to get a property of a member variable from reflection, but that property would not be available if it were a generic type.

Example:

public class Person { ... private Map<String,Integer> map; . } Class<Person> clazz = Person.class; Field f = clazz.getDeclaredField("map"); Println (" Map is of type: "+ f.getType()); GType = f.getGenericType(); If (ParameterizedType instanceof ParameterizedType) {ParameterizedType pType = (ParameterizedType)gType; RType = ptype.getrawType (); System.out.println(" the original type is: "+ rType); / / get the generic Type of a generic Type parameters [] gArgs = pType. GetActualTypeArguments (); // Print the generic argument for(int I =0; i < gArgs.length; I + +) {System. Out. Println (" first "+ I +" is a generic type is: "+ gArgs [I]); }} else {system.out.println (" Failed to get generic information "); }} interface java.util. Interface java.util.Map The 0th generic type is: class java.lang.String The 1st generic type is: class java.lang.IntegerCopy the code

Reflect source code and performance overhead

Only enumerate the source code of individual methods, other interested can check the source code (most of them are native methods)

Call the invoke() method

After obtaining the Method object, the invoke Method is called as follows:

As you can see, after calling method.invoke, methodAccessor.invoke is called directly. MethodAccessor is an instance of all the method shares of the same name mentioned above, created by the ReflectionFactory. The creation mechanism uses a method named inflation (after JDK1.4) : If the cumulative number of calls to this method <=15, NativeMethodAccessorImpl will be created, which is implemented by directly calling native methods to implement reflection. If the cumulative number of calls to the method is greater than 15, a MethodAccessorImpl assembled from bytecode is created from Java code. Call myClass.myMethod (String s). The resulting bytecode for MethodAccessorImpl is translated into Java code as follows:

public class GeneratedMethodAccessor1 extends MethodAccessorImpl {    
   public Object invoke(Object obj, Object[] args)  throws Exception {
       try {
           MyClass target = (MyClass) obj;
           String arg0 = (String) args[0];
           target.myMethod(arg0);
       } catch (Throwable t) {
           throw newInvocationTargetException(t); }}}Copy the code

As for the implementation of native methods, this paper will not discuss it because it is more in-depth

Contrast calling a method directly with calling it through reflection

    public static void main(String[] args) throws Exception {
       // directCall(); // Call directly
        reflectCall();// reflection calls
    }

    public static void target(int i) {}// Call directly
    private static void directCall(a) {
        long current = System.currentTimeMillis();
        for (int i = 1; i <= 2 _000_000_000; i++) {
            if (i % 100 _000_000= =0) {
                long temp = System.currentTimeMillis();
                System.out.println(temp - current);
                current = temp;
            }

            MethodTest.target(128); }}// reflection calls the same method
    private static void reflectCall(a) throws Exception { Class<? > klass = Class.forName("eft.reflex.MethodTest");
        Method method = klass.getMethod("target".int.class);

        long current = System.currentTimeMillis();
        for (int i = 1; i <= 2 _000_000_000; i++) {
            if (i % 100 _000_000= =0) {
                long temp = System.currentTimeMillis();
                System.out.println(temp - current);
                current = temp;
            }

            method.invoke(null.128); }} Run result: direct call result:...121
126
105
115
100(take the last5Values, as preheated after peak performance) reflection call results:...573
581
593
557
594(take the last5, as the peak performance after preheating)Copy the code

Result analysis: Ordinary calls as a performance baseline, about 100 seconds, through reflection calls take about 4 times as long as the baseline

Why does reflection incur performance overhead?

Take a look at the bytecode file called with reflection:

63: aload_1 // Load Method Object 64: aconst_null // static Method, reflection call the first argument is null 65: iconST_1 66: anewarray // generate an Object array of length 1 69: Dup 70: iconst_0 71: sipush 128 74: Invokestatic INTEger. valueOf 77: aastore Invokevirtual Method. Invoke // Reflection callCopy the code

You can see the two actions that precede the reflection call

  • Method.invoke is a variable-length parameter Method whose last parameter is an Object array at the bytecode level
    • The Java compiler generates an Object array of the length of the number of input arguments at the method call and stores one and one into the array
  • The Object array cannot store primitive types, and the Java compiler automatically boxes the passed primitive types

The above two steps introduce performance overhead and GC

How to reduce overhead?

    1. Increase the start the JVM parameters: – Djava. Lang. Integer. IntegerCache. High = 128, reduce packing

    After testing, peak performance: 280.4ms, 2.5 times of the benchmark time

    1. Reduce the automatic generation of Object arrays, the test code is as follows:
 private static void reflectCall(a) throws Exception { Class<? > klass = Class.forName("eft.reflex.MethodTest");
        Method method = klass.getMethod("target".int.class);

        // Construct an array of parameters outside the loop
        Object[] arg = new Object[1];
        arg[0] = 128;

        long current = System.currentTimeMillis();
        for (int i = 1; i <= 2 _000_000_000; i++) {
            if (i % 100 _000_000= =0) {
                long temp = System.currentTimeMillis();
                System.out.println(temp - current);
                current = temp;
            }

            method.invoke(null.128); }}Copy the code

Byte code:

80: aload_2 // Load Method object 81: aconst_null // static Method, reflection call the first argument is null 82: aload_3 83: Invokevirtual Method. Invoke // Reflection call, no anewarray instructionCopy the code

After testing, peak performance: 312.4ms, 2.8 times of the benchmark time

    1. Turn off inflation mechanism
    • -dsun.reflect. noInflation=true, if Inflation is turned off, reflection calls will be implemented dynamically from the start, instead of using delegate or native implementations.
    • Turn off permission verification: Each reflection call checks the permissions of the target method
// -Djava.lang.Integer.IntegerCache.high=128
// -Dsun.reflect.noInflation=true
public static void main(String[] args) throws Exception { Class<? > klass = Class.forName("eft.reflex.MethodTest");
        Method method = klass.getMethod("target".int.class);
        // Disable permission checking
        method.setAccessible(true);

        long current = System.currentTimeMillis();
        for (int i = 1; i <= 2 _000_000_000; i++) {
            if (i % 100 _000_000= =0) {
                long temp = System.currentTimeMillis();
                System.out.println(temp - current);
                current = temp;
            }

            method.invoke(null.128); }}Copy the code

Peak performance: 186.2ms, 1.7 times the baseline time

Advantages and disadvantages of reflection

advantages

1. Increase the flexibility of the program and avoid writing the program into code.

Ex. : Defines an interface that implements this interface class has 20, procedures used to the place where the implementation class has a lot of places, if not using a configuration file written code changes a lot, because it is not easy to change and to locate all the places, if you before you write the interface and implementation classes written in a configuration file, the next time you need to change the configuration file, This is done using reflection (which is already wrapped in the Java API, using class.newinstance ())

2. Simple code, improve code reuse rate, convenient external call

disadvantages

  • Performance overhead – Some Java virtual machine optimizations cannot be performed because reflection involves dynamically resolved types. Therefore, reflective operations perform worse than non-reflective operations and should be avoided in frequently invoked code segments in performance-sensitive applications.
  • Breaking encapsulation – Reflection can ignore permission checks when calling methods, thus potentially breaking encapsulation and causing security issues.
  • Obfuscating program logic – programmers want to see program logic in the source code, and techniques such as reflection bypass the source code, thus causing maintenance problems. Reflective code is more complex than the corresponding direct code.
  • Internal exposure – Because reflection allows code to perform operations that are illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can cause unexpected side effects that can cause code to malfunction and potentially break portability. Reflection code breaks abstraction and therefore may change behavior as the platform evolves.

Java reflection can access and modify private member variables, so does it make sense to wrap them private?

Since thieves can access and remove private members’ furniture, does it make sense to encapsulate it as a security door? This is the same reason, and Java from the application layer provides us with the security management mechanism, the security manager, each Java application can have its own security manager, it will be in the running stage check need to protect the resource access and other provisions of the operating rights, protect the system against malicious attacks operation, to achieve the system’s security policy. So reflection actually has internal security controls when it is used, and if the security Settings forbid these, then reflection cannot access private members.

Does reflection really slow down your program?

Reflection is about 50 to 100 times slower than a direct call, but you need to execute it a million times before you feel it 2. To judge the performance of a function, you need to execute the function a million or even 10 million times 3. If you only call reflection occasionally, forget about the performance impact of reflection. 4. If you need to call reflection a lot, consider caching. 5. Your programming mindset is the most important factor limiting your program’s performance

Scenarios using reflection in development

Factory mode: With reflection in the Factory class, you don’t need to modify the Factory class Factory after adding a new class, as shown in the following example

Through class.forname (Driver) in JDBC database. To get the database connection driver

Develop common frameworks – The most important use of reflection is to develop common frameworks. Many frameworks (such as Spring) are configured (such as javabeans, filters, etc., through XML files). To ensure the universality of the framework, they may need to load different objects or classes and call different methods based on the configuration file. This is where reflection is necessary — the runtime dynamically loads the objects that need to be loaded.

Dynamic proxy – In AOP, where specific methods need to be intercepted, dynamic proxies are typically chosen. This is where reflection technology comes in.

Annotation – the annotation itself is just a marker. It requires reflection to invoke the annotation interpreter based on the annotation tag to perform the behavior. Without reflection, annotations are no more useful than annotations.

Extensibility capabilities – Applications can use external user-defined classes by creating extensibility object instances with fully qualified names.

Examples of factory patterns using reflection:

// Implement factory mode with reflection mechanism:
interface fruit{
    public abstract void eat(a);
}
class Apple implements fruit{
    public void eat(a){
        System.out.println("Apple"); }}class Orange implements fruit{
    public void eat(a){
        System.out.println("Orange"); }}class Factory{
    public static fruit getInstance(String ClassName){
        fruit f=null;
        try{
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        returnf; }} Client:class hello{
    public static void main(String[] a){
        fruit f=Factory.getInstance("Reflect.Apple");
        if(f! =null){ f.eat(); }}}Copy the code

Reflection and introspection

Introspection (introspection) : Introspection-based implementation, that is, the repackaging of reflection, is used primarily to manipulate Javabeans by introspecting to get getters/setters for beans, colloquially: Javabeans have introspection mechanisms that allow you to set the values of javabeans without knowing what properties they have. The core is also reflection

In general, when developing frameworks, when you need to manipulate a JavaBean, it is very troublesome to use reflection all the time. So Sun developed a set of apis specifically for manipulating Javabeans

Introspection is the Java language’s default handling of Bean class attributes and events. For example, if there is A property name in class A, we can get its value by getName,setName or set A new value. The name attribute is accessed via getName/setName, which is the default rule. Java provides a set of apis for accessing getter/setter methods for a property. You don’t need to know this rule (but you’d better know it anyway), and these apis are stored in the package java.beans. The general approach is to get the BeanInfo of an object through the class Introspector, and then get the PropertyDescriptor through the BeanInfo, The property descriptor is used to get getter/setter methods for a property, which can then be invoked via reflection. Let’s look at an example that prints all the property names and values of an object:

package introspector;
// These apis are under java.beans (rt.jar).
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

public class IntrospectorDemo{
    String name;
    public static void main(String[] args) throws Exception{
        IntrospectorDemo demo = new IntrospectorDemo();
        // If you don't want to list the attributes of the parent class,
        // The second argument to getBeanInfo fills in the information for the parent class
        BeanInfo bi = Introspector.getBeanInfo(demo.getClass(), Object. class );// The Object class is the root parent of all Java classes
        PropertyDescriptor[] props = bi.getPropertyDescriptors();// Get the descriptor for the property
        for ( int i=0; i<props.length; i++){ System.out.println("Get attribute Class object:"+props[i].getPropertyType());
            props[i].getWriteMethod().invoke(demo, Hot and Sour soup );// Get the setName method and call it with invoke
            System.out.println("Read property value:"+props[i].getReadMethod().invoke(demo, null)); }}public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name; }} Get the Class object of the attribute:class java.lang.StringRead property value: hot and sour soupCopy the code

JDK Introspection class library: PropertyDescriptor Class: PropertyDescriptor class indicates that a JavaBean class exports a property from storage. Main methods: 1. GetPropertyType (), get the property of the Class object; 2. GetReadMethod (), obtain the method used to read attribute values; 3. GetWriteMethod (), get the method used to write the attribute value; 4. HashCode (), which gets the hash value of the object; 5. SetReadMethod (Method readMethod) specifies the Method used to read attribute values. 6. SetWriteMethod (Method writeMethod) : sets the Method for writing attribute values.

Apache has developed a set of simple, easy-to-use apis to manipulate Bean properties — the BeanUtils toolkit.

The resources

zhuanlan.zhihu.com/p/34168509

Fanyilun. Me / 2015/10/29 /…

Zhongmingmao. Me / 2018/12/20 /…