Java Foundation (class loading and ClassLoader understanding)

Keywords: Java

When a program actively uses a class, if the class has not been loaded into memory, the system will initialize the class through the following three steps

load

Load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a java.lang.Class object representing this class as the access entry for the class data in the method area (i.e. reference address). All class data that needs to be accessed and used can only be accessed through this class object. This loading process requires the participation of class loader.

link

The process of merging the binary code of a Java class into the running state of the JVM.

  1. Verification: ensure that the loaded class information conforms to the JVM specification. For example, starting with cafe, there is no security problem
  2. Preparation: the stage of formally allocating memory for class variables (static) and setting the default initial value of class variables. These memory will be allocated in the method area.
  3. Resolution: the process of replacing the symbolic reference (constant name) in the virtual machine constant pool with a direct reference (address).

initialization:

  1. The process of executing the class constructor () method. The class constructor () method is generated by the combination of the assignment action of all class variables in the class automatically collected at compile time and the statements in the static code block. (the class constructor constructs class information, not the constructor of the class object).
  2. When initializing a class, if it is found that its parent class has not been initialized, the initialization of its parent class needs to be triggered first.
  3. Virtual chance ensures that the () methods of a class are locked and synchronized correctly in a multithreaded environment.

Functions of class loader:

  1. The function of class loading: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a java.lang.Class object representing this class in the heap as the access entry to the class data in the method area.
  2. Class caching: the standard JavaSE class loader can find classes as required, but once a class is loaded into the class loader, it will remain loaded (cached) for a period of time. However, the JVM garbage collection mechanism can recycle these class objects.

Class loader is used to load classes into memory. The JVM specification defines class loaders of the following types.

ClassLoaderTest.java

package com.klvchen.java;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Properties;

public class ClassLoaderTest {

    @Test
    public void test1(){
        // For custom classes, use the system class loader to load them
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);
        //Call getParent() of the system class loader: get the extended class loader
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);
        //Calling getParent() of extension class loader: unable to get boot class loader
        //The bootstrap class loader is mainly responsible for the class library and cannot load custom classes.
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);

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

    }


    /*
      Properties: Used to read configuration files.
     */
    @Test
    public void test2() throws Exception {
        Properties pros = new Properties();
        //At this time, the file is under the current module by default
        //Method 1 for reading configuration files:
        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
        pros.load(fis);

        //The second way to read the configuration file is to use CLassLoader
        //The default identification of the configuration file is: 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);
    }
}

jdbc1.properties

user=wasp
password=abc123456

jdbc.properties

user=Wu Fei
password=abc123

NewInstanceTest.java

package com.klvchen.java;

import org.junit.Test;

import java.util.Random;

public class NewInstanceTest {

    @Test
    public void test1() throws Exception {
        Class<Person> clazz = Person.class;

        /*
        newInstance():Call this method to create the object of the corresponding runtime class. Internally, call the constructor of the null parameter of the runtime class

        To create the object of the runtime class normally with this method, you need to:
        1.The runtime class must provide a constructor with null parameters
        2.The constructor of null parameter has sufficient access rights. Usually, it is set to public

        A public null parameter constructor is required in the java bean. Reason:
        1.It is convenient to create objects of runtime classes through reflection
        2.When a subclass inherits this runtime class, it is guaranteed that the parent class has this constructor when super() is called by default

         */
        Person obj = clazz.newInstance();
        System.out.println(obj);
    }

    //Dynamics of reflection
    @Test
    public void test2(){
        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.klvchen.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:Specifies the full class name of the class
     */
    public Object getInstance(String classPath) throws Exception {
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }
}

Person.java

package com.klvchen.java;

public class Person {

    private String name;
    public int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + 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()");
    }

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void show() {
        System.out.println("Hello, I'm alone");
    }

    public String showNation(String nation) {
        System.out.println("My nationality is: " + nation);
        return nation;
    }
}

ReflectionTest.java

package com.klvchen.java;

import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionTest {

    // Operation on Person before reflection
    @Test
    public void test1(){
        //1. Create the object of Person class
        Person p1 = new Person("Tom", 12);

        //2. Call its internal properties and methods through the object
        p1.age = 10;
        System.out.println(p1.toString());

        p1.show();

        //Outside the Person class, its internal private structure cannot be called through the object of the Person class.
        //For example: name. Shownation () and private constructor

    }

    //After reflection, the operation of Person
    @Test
    public void test2() throws Exception {

        Class clazz = Person.class;
        //1. Create the object of Person class through reflection
        Constructor cons = clazz.getConstructor(String.class, int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person) obj;
        System.out.println(p.toString());

        //2. Call the properties and methods specified by the object through reflection
        //Call properties
        Field age = clazz.getDeclaredField("age");
        age.set(p, 10);
        System.out.println(p.toString());

        //Call method
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);

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

        //Through reflection, you can call the private structure of the Person class, such as private constructors, methods, and properties
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person) cons1.newInstance("Jerry");
        System.out.println(p1);

        //Call private properties
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1, "HanMeimei");
        System.out.println(p1);

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

    }

    /*
    Understanding of java.lang.Class class
    1.Class loading process:
      After the program passes the javac.exe command, it will generate one or more bytecode files (. End of class).
      Then we use the java.exe command to interpret and run a bytecode file, which is equivalent to loading a bytecode file into memory.
      This process is called Class loading. The Class loaded into memory is called the runtime Class, which is used as an instance of Class.
    2.In other words, an instance of Class corresponds to a runtime Class.
    3.The runtime class loaded into memory will be cached for a certain time. Within this time, we can obtain this runtime class in different ways.
     */

    // How to get an instance of Class
    @Test
    public void test3() throws ClassNotFoundException {
        //Method 1: call the attribute of the runtime class:. Class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //Method 2: call getclass() through the object of the runtime class
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        //Method 3: call the static method of Class: forName(String classPath)
        Class clazz3 = Class.forName("com.klvchen.java.Person");
        //Class clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

        //Method 4: use class loader: ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.klvchen.java.Person");
        System.out.println(clazz4);

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

    }

    @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 is the same as the dimension, it is the same class
        System.out.println(c10 == c11);
    }
}

Posted by luddeb on Tue, 30 Nov 2021 19:28:51 -0800