Java reflection

Overview of Java Reflection Mechanism

What is reflection:

(1) The core of Java reflection mechanism is to dynamically load classes and obtain detailed information about them while the program is running, thereby manipulating the properties and methods of classes or objects. Essentially, after the JVM gets the class object, it decompiles it to get all kinds of information about the object.

(2) Java belongs to the language that compiles and runs first. The type of object in the program is determined at compile time. While the program may need to load some classes dynamically at run time, they are not loaded into JVM because they were not used before. Reflection allows you to dynamically create objects and invoke their properties at runtime without having to know who the running object is at compile time in advance.

  • Reflection is the key to what is considered a dynamic language, and the reflection mechanism allows the program to run at execution time
    Use the Reflection API to get the internal information of any class and directly manipulate the inside of any object
    Part properties and methods.
  • After loading the class, a Class-type object (one) is generated in the method area of heap memory
    Class has only one Class object, which contains the complete class structure information. We can
    You can see the structure of the class through this object. This object is like a mirror through which you can see
    To the structure of the class, so our image is called reflection.

A schematic illustration of reflection:
The following illustration shows the normal loading process of a class, the principle of reflection, and the class object:

Class objects have always been. The class file reads into memory and creates a Class object for it.

Functions provided by the reflection mechanism
1. Decompilation: class->. Java
2. Accessing properties, methods, construction methods of java objects through reflection mechanism
3. When we use IDE, such as Ecplise, when we enter an object or class and want to call its properties and methods, the compiler automatically lists its properties or methods by dot, where reflection is used.
4. The most important use of reflection is to develop common frameworks. For example, many frameworks (Spring) are configurable (such as configuring beans through XML files). To ensure the universality of the framework, they may need to load different classes or objects according to the configuration file and invoke different methods, at which point they must use reflection, which dynamically loads the objects they need to load at runtime.
5. For example, during the development process using the Strut2 framework, we typically use struts. Configure actions in xml, such as

<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">   
    <result>/shop/shop-index.jsp</result>           
    <result name="error">login.jsp</result>       
</action>

For example, we ask login. When action occurs, StrutsPrepareAndExecuteFilter resolves struts.xml file, find the action with name login from the action, create a SimpleLoginAction instance based on the class attribute, and call the execute method with the Invoke method, which is inseparable from reflection. The configuration file establishes a mapping relationship with the Action, and when the View layer makes a request, the request is intercepted by the StrutsPrepareAndExecuteFilter, which then dynamically creates the Action instance.

For example, loading database-driven also uses reflection.
Class.forName("com.mysql.jdbc.Driver"); // Dynamic loading MySQL driver

  • Determine which class any object belongs to at run time
  • Construct objects of any class at run time
  • Determine at runtime which member variables and methods any class has
  • Getting generic information at run time
  • Call member variables and methods of any object at run time
  • Processing annotations at run time
  • Generate dynamic proxy

Advantages and disadvantages of reflection:
1. Advantages: Get all the contents of the class at runtime and decompile it. Java, a compiled-and-run language, allows us to easily create flexible code that can be assembled at runtime without linking source code between components, making it easier to implement object-oriented.

2. Disadvantages: (1) Reflection consumes certain system resources, so it is not necessary to create an object dynamically;

(2) Privilege checks can be ignored when reflecting the invocation of a method, thus potentially breaking the encapsulation and causing security problems.

Classes commonly used for reflection mechanisms

  • java.lang.Class: Represents a class
  • java.lang.reflect.Method:Method representing class
  • java.lang.reflect.Field:Member variable representing class
  • java.lang.reflect.Constructor:Constructor representing class
  • java.lang.reflect.Modifier; Represents static methods and constants
  • ......

Supplement: Dynamic Language vs Static Language

1. Dynamic Language
A language that can change its structure at run time: for example, new functions, objects, or even code can be introduced, existing functions can be deleted, or other structural changes. Common sense is that code can change its structure under certain conditions at runtime.
Main dynamic languages: Object-C, C#, JavaScript, PHP, Python, Erlang.
2. Static Language
Corresponding to dynamic languages, static languages are those that have an inflexible runtime structure. For example, Java, C, C++.

Java is not a dynamic language, but Java can be called a "quasi-dynamic language". That is, Java is dynamic and we can use reflection mechanism and byte code operation to get similar dynamic language features. Java's dynamics make programming more flexible!

Example

Person class

public class Person {
    private String name;
    public int age;

    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
    
    public Person() {System.out.println("Person()");}
    public Person(String name, int age) { this.name = name;this.age = age;}
    private Person(String name) {this.name = name;}

    public void show(){ System.out.println("Hello, I'm a person");}
    private String showNation(String nation){
        System.out.println("My nationality is:" + nation);
        return nation;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Common Instance Creation Methods

  1. Create Object of Person Class
  2. By object, call its internal properties, methods

Outside the Person class, its internal private structure cannot be invoked through an object of the Person class.
Examples include name, showNation(), and private constructors

Person p1 = new Person("Tom", 12);
p1.age = 10;
System.out.println(p1.toString());

Use reflection to operate on Person, call properties, methods, and also call private constructors, properties, methods

public void test2() throws Exception{
        //1. Create objects of Person class by reflection
        Class clazz = Person.class;
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person) obj;
        System.out.println(p.toString());//Person{name='Tom', age=12}
        //2. Call object-specified properties and methods through reflection
        //Call Properties
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p.toString());//Person{name='Tom', age=10}

        //Call Method
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);//Hello, I'm a person
        System.out.println("***************************************************");
        //Reflection allows you to call the private structure of the Person class. For example: private constructors, methods, properties
        //Invoke Private Constructor
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person) cons1.newInstance("Jerry");
        System.out.println(p1);//Person{name='Jerry', age=0}

        //Invoke Private Properties
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"HanMeimei");
        System.out.println(p1);//Person{name='HanMeimei', age=0}

        //Invoke private methods
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation = (String) showNation.invoke(p1,"China");//My nationality is: China
        //Equivalent to String nation = p1.showNation("China")
        System.out.println(nation);//China


    }

Doubt

  1. Public structures can be invoked either directly new or reflected, so which one is used in development?
    Suggestion: Direct new approach.
    When will reflection be used 1 : Characteristics of reflection: Dynamics
  2. Is the reflection mechanism contradictory to encapsulation in object-oriented? What about the two technologies?
    Not contradictory.

Understanding Class classes and getting Class instances*

  1. Class loading process:
    The program passes through javac. After the EXE command, one or more byte code files (.class ends) are generated. Then we use java. The exe command interprets a byte code file. Equivalent to loading a byte code file into memory. This process is called class loading. A class loaded into memory is called a runtime class, which acts as an instance of a Class.
  2. In other words, an instance of Class corresponds to a runtime class.
  3. Runtime classes loaded into memory are cached for a certain amount of time. During this time, we can do it in different ways
    To get this runtime class.

Common methods of Class classes

Four ways to get an instance of a Class class (the first three are required)

(1) Any data type, including the basic data type, has a "static" class attribute

(2)Object–>getClass

(3) Static methods via class classes: forName(String className) (most commonly used)

        //Mode 1: Call the properties of the runtime class:. Class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //Mode 2: Call getClass() through the object of the runtime class
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);
        //Mode 3: Call Class static method: forName(String classPath)
        Class clazz3 = Class.forName("com.atguigu.java.Person");
//      clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);//true
        System.out.println(clazz1 == clazz3);//true
        //Mode 4: Use the loader for classes: ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
        System.out.println(clazz4);

        System.out.println(clazz1 == clazz4);//true

    }

Note that during run time, only one Class object is generated for a class, so the printed results are true;
Four ways, the third is common. The first one needs to import class packages. It relies too much on them and throws compilation errors without them. The second kind of object has what to reflect, generally using the third kind, a string can be passed in or written in the configuration file and other methods.

Which types can have Class objects?

(1) class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
(2) interface: interface
(3) []: Array
(4) enum: enumeration
(5) annotation: comment@interface
(6) primitive type: basic data type
(7)void

    @Test
    public void test4(){
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;
        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        // As long as the element type of the array is the same as the dimension, it is the same Class
        System.out.println(c10 == c11);

Class loading and understanding of lassLoader

Loading of classes (Understanding)



public class ClassLoadingTest {
	public static void main(String[] args) {
		System.out.println(A.m);
	}
}
class A {
	static {
		m = 300;
	}
	static int m = 100;
}
//Step 2: m=0 after the link ends
//Step 3: After initialization, the value of m is determined by the <clinit>() method execution
//The class constructor <clinit>() method for this A is merged sequentially by assignment of class variables and statements in static code blocks
//Produce, similar to
// <clinit>(){
// m = 300;
// m = 100;
// }

When will class initialization occur? (Understanding)



ClassLoader (Understanding)

Parent Delegation Mechanism for Class Loader

//1. Get a system class loader
	ClassLoader classloader = ClassLoader.getSystemClassLoader();
	System.out.println(classloader);
//2. Get the parent class loader of the system class loader, the extension class loader
	classloader = classloader.getParent();
	System.out.println(classloader);
//3. Get the parent class loader of the extension class loader, the boot class loader
	classloader = classloader.getParent();
	System.out.println(classloader);
//4. Test which class loader loads the current class
	classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
	System.out.println(classloader);
//5. Test which class loader loads the Object class provided by JDK
	classloader = Class.forName("java.lang.Object").getClassLoader();
	System.out.println(classloader);
//*6. One of the main methods for class loaders: getResourceAsStream(String str): Get the input stream of the specified file under the class path
	InputStream in = null;
	in =
	this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
	System.out.println(in);

Example

public class ClassLoaderTest {
    @Test
    public void test1(){
        //For custom classes, use the System Class Loader to load
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //Call getParent() of the system class loader: get the extension class loader
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@78308db1
        //getParent() calling extension class loader: cannot get boot class loader
        //The boot class loader is mainly responsible for loading the core class libraries of java and cannot load custom classes.
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//null

        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3);//null
    }
}

Loading configuration files using ClassLoader

	 @Test
  	  public void test2() throws Exception {
        Properties pros =  new Properties();
        //At this point, the file defaults to the current module.
        //Read the configuration file in one way:
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
//        pros.load(fis);

        //Reading configuration files in two ways: using ClassLoader
        //Configuration files are recognized by default as: under the src of the current module
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);
        
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user = " + user + ",password = " + password);
    }
Authors John Luke

Create object of runtime class*

With a Class object, you can
Create the object of the corresponding runtime class: Call the newInstance() method of the Class object

Requirement 1) Runtime class must provide a constructor with empty parameters 2) The class constructor needs sufficient access. Typically set to public.

A public empty parameter constructor is required in javabean. Reason:
1. Easy to create runtime class objects through reflection
2. Ensure that the parent class has this constructor when super() is called by default when it is convenient for the child class to inherit this runtime class

Can't an object be created without a parameterless constructor?
No The operation can be instantiated as long as the constructor in the class is explicitly called during the operation and the parameters are passed in.

The steps are as follows 1) through Class class getDeclaredConstructor(Class... parameterTypes) Gets the constructor for the specified parameter type of this class 2) Pass an array of objects into the constructor's parameters, which contain the parameters required in the constructor. 3) Instantiate objects through Constructor.

These are the places where reflection mechanisms are used the most

//1. Get the corresponding Class object based on the whole class name
	String name = "atguigu.java.Person";
	Class clazz = null;
	clazz = Class.forName(name);
//2. Call the constructor of the specified parameter structure to generate an instance of the Constructor
	Constructor con = clazz.getConstructor(String.class,Integer.class);
//3. Create objects of corresponding classes through instances of Constructor and initialize class properties
	Person p2 = (Person) con.newInstance("Peter",20);
	System.out.println(p2);

Example

Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();//Person()
        System.out.println(obj);//Person{name='null', age=0}

Experience dynamic instances of reflection

@Test
    public void test2(){
        for(int i = 0;i < 100;i++){
            int num = new Random().nextInt(3);//0,1,2
            String classPath = "";
            switch(num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.atguigu.java.Person";
                    break;
            }
            try {
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /*
    Creates an object of the specified class.
    classPath:Full class name of specified class
     */
    public Object getInstance(String classPath) throws Exception {
       Class clazz =  Class.forName(classPath);
       return clazz.newInstance();
    }

Get the complete structure of the runtime class

Provide structurally rich classes

Person class

@MyAnnotation(value="hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{
    private String name;
    int age;
    public int id;
    
    public Person(){}
    @MyAnnotation(value="abc")
    private Person(String name){this.name = name;}
 		    Person(String name,int age){this.name = name;this.age = age;}
 		    
    @MyAnnotation
    private String show(String nation){
        System.out.println("My nationality is:" + nation);
        return nation;
    }
    public String display(String interests,int age) throws NullPointerException,ClassCastException{
        return interests + age;
    }
    @Override
    public void info() {System.out.println("I am a person");}
    @Override
    public int compareTo(String o) {return 0;}
    private static void showDesc(){System.out.println("I am a lovely person");}
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }

Creature class

public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    private void breath(){System.out.println("Biological Respiration");}
    public void eat(){System.out.println("Biological Eating");}

}

MyInterface

public interface MyInterface {void info();}

MyAnnotation

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {String value() default "hello";}

Gets the property structure of the current runtime class

public class FieldTest {
    @Test
    public void test1(){
        Class clazz = Person.class;

        //Get Attribute Structure
        //getFields(): Gets the properties declared as public access rights in the current runtime class and its parent class
        Field[] fields = clazz.getFields();
        for(Field f : fields){
            System.out.println(f);
        }
        System.out.println();

        //getDeclaredFields(): Gets all properties declared in the current runtime class. (does not contain properties declared in the parent class)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            System.out.println(f);
        }
    }

    //Permission modifier data type variable name
    @Test
    public void test2(){
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            //1. Permission modifiers
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + "\t");

            //2. Data type
            Class type = f.getType();
            System.out.print(type.getName() + "\t");

            //3. Variable Name
            String fName = f.getName();
            System.out.print(fName);

            System.out.println();
        }
    }
}

Call the specified structure of the runtime class*

Application of reflection: dynamic proxy

  1. If we started compiling, we would not be able to determine who objects I would like to create now. For example, in this code, this is directly a new person. If it is written incorrectly, it will be wrong to write person1. This is equivalent to compiling time, so check it. That is, the compilation time has already been checked, that is, the compilation time, which actually determines who you want to create. In this way, it is also the case in the vast majority of development. That's not to exclude some cases, when compiling, I can't decide which kind of object to build. Not excluded. When compiling, you don't know which class of objects you want to use, so if that happens, use reflection.
    For a simple example, background also involves this front-end, then the back-end, the code has actually been written, all deployed to the server are running. The servers are all running. When you access the background through the browser, the function may be logged on, it may be registered, you don't know what to do, when the server runs, it means the code has already run. During this running period, you don't know which kind of object to build, because you don't know whether you want to register or log on, and what to send. You can call related methods by the object of the related class, which is called dynamic ↩︎

Keywords: Java Back-end

Added by Zoud on Sun, 02 Jan 2022 21:34:30 +0200