Again, plugins, but this time it’s reflection. The two foundations of plug-in are dynamic proxy and reflection, which we talked about last time.

Let’s start with the Java memory model, which is the memory of the Java virtual machine at runtime. Runtime memory is divided into thread-private and thread-shared memory.

Thread private program counters, virtual machine stacks, local method stacks, thread shared method areas (including runtime constant pools), and the Java heap.

Java memory is divided into heap and stack, corresponding to the top heap and virtual machine stack respectively.

* the program counter: * Java allows multiple threads to execute instructions at the same time, if there are multiple threads execute instructions at the same time, so each thread has a program counter, at any time, a thread is only allowed to execute a method code, when executed to a Java method code, the program counter to save the current execution bytecode address, If the native method is executed, the value of PC is undefined.

* Virtual machine stack :* Describes the memory model of Java method execution. Each method will create a frame stack during execution, which is used to store local variable table, operand stack, dynamic link, method exit and other information. Each method from invocation to completion, corresponds to the process of a frame stack from stack to stack.

* Native method Stack :* Provides memory space for Native methods used by virtual machines. Native method Stack uses traditional C Stack to support Native methods.

* Java heap :* Provides the memory area for thread sharing. It is the largest memory area managed by the Java VIRTUAL machine and the main area for GC. Almost all object instances and array instances are allocated on the Java heap. The Size of the Java heap can be fixed, or it can grow as needed and automatically shrink when it is no longer needed.

* Method area :* Stores the class information that has been loaded by the virtual machine, constants, static variables, compiler compiled code and other data.

* Runtime constant pool :* Holds literal and symbolic references generated by the compiler.

1. What is reflection

Reflection is a feature of the Java language that allows a running program to retrieve information about itself and manipulate the internal properties of classes and objects. The Java Reflection framework provides the following functions:

  • 1. Determine the class of any object at runtime;
  • 2. Construct an object of any class at runtime;
  • 3. Determine which member variables and methods any class has at run time (you can even call private methods through reflection);
  • 4. Call a method of any object at runtime;

2. Use of reflection

  • 1. When we use an IDE, when we input an object and want to call its properties and methods, as soon as we press the dot, the compiler will automatically list its properties and methods, which is used here.
  • 2. General framework. Many frameworks are configured (for example, Spring configures beans or actions through XML). In order to ensure the universality of the framework, it may be necessary to load different objects or classes and call different methods according to different configuration files.

3. Basic use of reflection

Some of the functionality provided was mentioned above, fetching classes and calling class properties or methods.

3.1. Get the Class object

There are three methods:

  • Use the static method of Class
try {
    Class.forName("");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
Copy the code
  • Get the Class of an object directly
public class Reflection { private void getClazz() { Class<Reflection> c = Reflection.class; Class<String> s = String.class; Class<Integer> i = int.class; }}Copy the code
  • Call the getClass method of an object
ArrayList list = new ArrayList(); Class<? > l = list.getClass();Copy the code
3.2. Determine whether it is an instance of a class

We usually use instanceof, but we can also use class.isinstance (obj).

StringBuilder sb = new StringBuilder(); Class<? > c = sb.getClass(); System.out.println(c.isInstance(sb));Copy the code
3.3. Create an instance

There are two main ways to use reflection to generate objects.

  • useClass.newInstancemethods

This method ends up calling a no-argument constructor, so an error is reported if the object does not have a no-argument constructor. Using newInstance requires: 1. The class is loaded. 2. This class is already connected. NewInstance () actually splits new into two steps: first load a Class using a classload method and then instantiate it. Of course constructors cannot be private.

Class<Reflection> c = Reflection.class;
try {
    Reflection r = c.newInstance();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}
Copy the code

– Obtains the specified Constructor object from the Class object and then calls the Constructor object’s newInstance() method to create the instance. This method constructs an instance of a class with the specified constructor. Of course constructors cannot be private.

Class<String> s = String.class;
try {
    Constructor constructor = s.getConstructor(String.class);
    try {
        Object o = constructor.newInstance("378");
        System.out.println(o);
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}
Copy the code
3.4. Acquisition Method (Method)

There are several ways to get a collection of methods for a Class object.

public Method[] getDeclaredMethods() throws SecurityException
Copy the code

Can get its own public, protected, default, private methods, but not inherited methods.

public Method[] getMethods() throws SecurityException
Copy the code

Methods that can be obtained for public and inherited implementations.

public Method getDeclaredMethod(String name, Class<? >... parameterTypes) throws NoSuchMethodException, SecurityExceptionCopy the code

You can obtain specific public, protected, default, and private methods of your own, but not inherited methods.

public Method getMethod(String name, Class<? >... parameterTypes) throws NoSuchMethodException, SecurityExceptionCopy the code

Methods that can get specific public and inherited implementations.

3.5. Get Constructor information

Through the Class object’s getConstructor method.

Class<String> s = String.class;
try {
    Constructor constructor = s.getConstructor(String.class);
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}
Copy the code
3.6. Obtaining Member Variable Information (Field)

GetFiled: Access public member variables getDeclaredField: All declared member variables. GetFileds and getDeclaredFields do not get the member variables of their parent class.

3.7. Invoke a method

When we get a method from a class, we invoke the method with the invoke() method.

public class Reflection { private String mm; public Reflection(String v) { mm = v; } public static void main(String[] ps) { runMethod(); } private void in() { System.out.println(mm); } private static void runMethod() { Class<Reflection> c = Reflection.class; try { Constructor constructor = c.getConstructor(String.class); try { Object o = constructor.newInstance("378"); Method method = c.getDeclaredMethod("in", (Class<? >[]) null); method.setAccessible(true); method.invoke(o, (Object[]) null); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); }}}Copy the code

The Invoke method is used to dynamically invoke the methods of an instance at runtime. The Invoke method first checks the value of the Override property of AccessibleObject. The AccessibleObject class is the base class for Field, Method, and Constructor objects.

It provides the ability to mark reflected objects for use without default Java language access control checks. The default value of Override is false, indicating that the permission call rule is required and the permission must be checked when the method is called.

We can also use the setAccessible method to set this value to true. If override is set to true, the permission rule is ignored and no permission is checked when calling the method (that is, any private method can be called, violating encapsulation).

3.8. Create arrays using reflection

Arrays are a special type in Java that can be assigned to an Object Reference.

public static void createArray() throws ClassNotFoundException {
    Class<?> cls = Class.forName("java.lang.String");
    Object array = Array.newInstance(cls, 3); // 等价于 new String[3];
    //往数组里添加内容
    Array.set(array, 0, "OK");
    Array.set(array, 1, "HOW ARE YOU");
    Array.set(array, 2, "Fine");
    //获取某一项的内容
    System.out.println(Array.get(array, 2)); // 等价于array[2]
}
Copy the code
3.9. Generic handling

Since the introduction of generics in Java 5, the Java Reflection API has been modified to provide support for generics. Due to type erasers, information such as type parameters in generic classes is not available at runtime. All the JVM sees are primitive types.

private List<String> genericTypeValue = new ArrayList<String>(); private List nullGenericType; public void testGenericType() throws SecurityException, NoSuchFieldException, InstantiationException, IllegalAccessException{// If the class attribute has a type parameter, such as List<T> // then to get type T, use field.getGenericType(); ParameterizedType Field genericTypeField1 = Clazz.getDeclaredField ("genericTypeValue"); Field genericTypeField2 = clazz.getDeclaredField("nullGenericType"); ParameterizedType genericType1 = (ParameterizedType)genericTypeField1.getGenericType(); // nullGenericType has no parameter type. Casting to (ParameterizedType) will throw an exception! // can only be converted to (Class<? >) or through the getType () to obtain type / / ParameterizedType genericType2 = (ParameterizedType) genericTypeField2. GetGenericType (); Class<? > type1 = genericTypeField1.getType(); //type1 is the type of List<String>! Class<? > Type2 = (Class<? >)genericTypeField2.getGenericType(); Class<? > Type2_1 = genericTypeField2.getType(); / / by parameterized Type [ParameterizedType] to obtain the parameters of the statement Type of array Type [] types1. = genericType1 getActualTypeArguments (); Class<? > typeValue1 = (Class<? >) types1[0]; System.out.println("typeValue1:"+typeValue1); //class test.String System.out.println("typeValue2:"+Type2); //interface java.util.List System.out.println("typeValue2_1:"+Type2_1); //interface java.util.List if(typeValue1.equals(String.class)) //true System.out.println("typeValue1.equals(String.class)?" +typeValue1.equals(String.class)); if(Type2.equals(List.class)) //true System.out.println("Type2.equals(List.class)?" +Type2.equals(List.class)); // Create an object of type containing the parameter type. // ArrayList<String> newInstance = (ArrayList<String>) type1.newinstance (); // newInstance.add("123"); }Copy the code

4. Reflection optimization

4.1. To make good use of the API

For example, instead of using getMethods() and then iterating through filters, use getMethod(methodName) to getMethods by methodName.

4.2. Good caching method

For example, when you need to dynamically create instances of a class multiple times, writing with caching is much faster than writing without caching. Also cache the method/field/constructor object from reflection.

Void createInstance(String className){return class.forName (className).newinstance (); // 1. Void createInstance(String className){cachedClass = cache.get(className); if (cachedClass == null){ cachedClass = Class.forName(className); cache.set(className, cachedClass); } return cachedClass.newInstance(); }Copy the code

Why is that? Of course, because forName is too time-consuming. Cache must be implemented by itself.

4.3. Try to use a higher version of the JDK
4.4. Use a reflection framework

For example, Joor, or Apach](github.com/jOOQ/jOOR)… BeanUtils JAVAASSIST.

4.5.ReflectASMSpeed up reflection by means of bytecode generation

ASM is a Java bytecode manipulation framework. It can be used to dynamically generate classes or enhance the functionality of existing classes. ASM can either generate binary class files directly or dynamically change the behavior of classes before they are loaded into the Java virtual machine.

Java classes are stored in rigorously formatted.class files that have enough metadata to parse all the elements of the class: class names, methods, attributes, and Java bytecodes (instructions). After reading information from class files, ASM can change class behavior, analyze class information, and even generate new classes based on user requirements.

Java source code behaves differently in source code and compiled classes.

The type descriptors for Java types are listed below:

Java type Type Descriptor instructions
   boolean        Z     B is occupied by byte
  char       C     instructions
  byte       B     instructions
  short        S     instructions
   int        I     instructions
  long        J     Instead of L, L is occupied by the object’s type descriptor
  float        F     instructions
   double        D     instructions
  void        V     instructions
An array of    [     The character starts with [and other special characters denote arrays of the corresponding data type. Several [denote arrays of several dimensions
Reference types L Full class name; Start with L; The end is followed by the full class name of the reference type
methods (Parameter Type Parameter Type) Return type Methods are described in parentheses, with parameters inside the parentheses, followed by return types to the right

Example field descriptor

The descriptor Field declarations
    I        int i   
    [[J        long[][] xi   
    [Ljava/lang/Object;        Object[] obj   
    Ljava/util/Hashtable;        Hashtable tab   
    [[[Z        boolean[][][] re   

Example method descriptor

The descriptor Method statement
    ()I        int getCount()   
    ()Ljava/lang/String;        String getDesc()   
    ([Ljava/lang/String;)V        void sp(String[] s)   
    (J)Ljava/lang/String;        String ltostr(long t)   
    (JI)V        void wait(long t,int count)   
    ([BJI)I        int wit(byte[] t,long l,int i)   
    (Z[Ljava/lang/String;II)Z       boolean should(boolean ig,String s,int i,int j)   

Execute javap -s java.lang.String to see all the method signatures of java.lang.StringExample:

public class Asmain { public static void main(String[] args) { ClassVisitor visitor = new ClassVisitor(Opcodes.ASM5) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); Println (superName + "" + name); system.out.println (superName +" "+ name); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {// Print out the method name and type signature system.out.println (name + "" + desc); return super.visitMethod(access, name, desc, signature, exceptions); }}; // Read static inner class cr = null; try { cr = new ClassReader("com.yong.reflection.asm.Asmain$Sam"); cr.accept(visitor, 0); } catch (IOException e) { e.printStackTrace(); } } static class Sam { private String name; public Sam(String name) { this.name = name; } private long getAge() { return 25; } private void Say() {system.out.println () {system.out.println (); ); }}}Copy the code

Output:Then we can add methods to the Sam class:

public void addedMethod(String str) {
}
Copy the code

Use the ClassWriter

try { ClassReader classReader = new ClassReader(Sam.class.getName()); ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); classReader.accept(classWriter, Opcodes.ASM5); MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC, "addedMethod", "(Ljava/lang/String;) V", null, null); mv.visitInsn(Opcodes.RETURN); mv.visitEnd(); Byte [] code = classwriter.tobytearray (); / / write binary stream to directory FileOutputStream fos = new FileOutputStream (". / javareflection/Sm. Class "); fos.write(code); fos.close(); } catch (IOException e) { e.printStackTrace(); }Copy the code

Look at the generated code: