The JVM and class

When a Java program is run by calling a Java command, the command will start a Java virtual Machine process, and no matter how complex the Java program is or how many threads the program starts, they are all in the Java Virtual Machine process. All threads and variables of the same JVM are in the same process, and they all use the memory area of that JVM process. The JVM process will be terminated when one of the following conditions occurs:

  • The program runs to the final normal end;
  • The program runs to end the program using system.exit () or runtime.geTruntime.exit () code;
  • The program execution process encountered uncaught exception or error and end;
  • The platform where the application is running forces the JVM process to terminate

When a Java program ends, the JVM process terminates, and the process’s state in memory is lost:

public class A
{
	public static int a = 6;  // Define class variables for this class
}
Copy the code
public class TestA
{
	public static void main(String[] args)
	{
		A a = new A();
		a.a++;
		System.out.println(a.a);   / / output 7}}Copy the code
public class TestA2
{
	public static void main(String[] args)
	{
		A b = new A();
		System.out.println(b.a);   / / output 6}}Copy the code

Running TestA and TestA2 runs the JVM process twice. After the first run of the JVM, all the changes it made to class A are lost —– The second run of the JVM initializes class A again;

No data is shared between the two JVMS;

Class lifecycle

The entire life cycle of a class from the time it is loaded into the memory of the virtual machine to the time it is unloaded includes:

  1. Loading
  2. Verification
  3. Preparation (Preparation)
  4. Resolution (Resolution)
  5. Initialization
  6. Using
  7. Unloading (class)

There are 7 stages, among which verification, preparation and parsing are collectively called Linking. The occurrence order of these 7 stages is as follows:

The load, validation, preparation, initialization, and unload phases are in a fixed order. The class loading process must start in this order, while the parsing phase is not necessarily: it can start after the initialization phase in some cases, to support runtime binding of the Java language;

When do you need to start the first stage of the class loading process: loading? There is no mandatory constraint in the Java virtual machine specification and this can be left to the implementation of the virtual machine. For the initialization phase, however, the VM specification specifies that there are only five conditions in which a class must be initialized immediately:

  1. When you encounter the four bytecode instructions new, getStatic, putStatic, or Invokestatic, you need to trigger the initialization if the class has not been initialized before. The most common Java code scenarios for generating these four instructions are when instantiating an object with the new keyword, when reading or setting a static field of a class (except for static fields that are final and have been put into a constant pool at compile time), and when calling a static method of a class;
  2. Using Java,.. When the lang.Reflect package method is called to reflect on a class that has not already been initialized, the class needs to be initialized first.
  3. When initializing a class, if the parent class has not been initialized, the parent class must be initialized first.
  4. When the VM starts, the user needs to specify a main class to execute (the class containing the main() method), which the VM initializes first.
  5. When using JDK1.7 dynamic language support, if a Java lang. Invoke. The final analytical results REF_getStatic MethodHandle instance, REF_putStatic, REF_invokeStatic method handles, If the class to which the method handle corresponds has not been initialized, it needs to be initialized first.

These five scenarios are called active references to a class. In addition, any reference to a class does not trigger initialization. This is called a passive reference.

Example 1:

public class SuperClass {
	static {
		System.out.println("SuperClass init!");
	}
	
	public static int valeu = 123;
}
class SubClass extends SuperClass{
	static {
		System.out.println("SubClass init!"); }}class NotInitializating{
	public static void main(String[] args) { System.out.println(SubClass.valeu); }}/ / output
SuperClass init!
123
Copy the code

For static fields, only the class that defines the field directly is initialized, so referencing a static field defined in a parent class through its subclass triggers initialization only for the parent class and not for the subclass.

Cite example 2 passively

public class ConstClass {
	static {
		System.out.println("ConstClass init!");
	}
	
	public static final int valeu = 123;
}


class NotInitializating{
	public static void main(String[] args) { System.out.println(ConstClass.valeu); }}/ / output
123
Copy the code

No output “ConstClass init!” This is because the constant valeu in the ConstClass class is referred to in the Java source code, but the value 123 is stored in the NotInitializating class constant pool at compile time through constant propagation optimization. Later NotInitializating references to constclass.valeu are actually converted to NotInitializating class references to its own constant pool. That is, there is actually no symbolic reference to a ConstClass in the NotInitializating Class file. The two classes are not related to each other after they are written as a Class.

Class loading

When a class is needed in a Java program, the virtual machine ensures that the class has been loaded, wired, and initialized. Concatenation, in turn, consists of three sub-processes: validation, preparation, and parsing, which must be performed in strict order

Class loading refers to reading the class file of a class into memory and creating a Java.lang. class object for it.Class loading is done by class loaders, which are usually provided by the JVM. Classloaders generally do not have to wait until the class is “first called” to load it. The Java Virtual Machine specification allows the system to pre-load some classes;

Connection of a class

Once the Class is loaded, the system generates a corresponding Class object for it, which then leads to the connection phase, which merges the binaries of the Class into the JRE. Class concatenation can be divided into three phases:

  • Validation: Ensure that the loaded class is correct. The byte stream of the class file contains information that meets the requirements of the current VM without compromising vm security. (for example, using UE, editPlus or other binary editor to change the magic number of helloWorld.class, the console will run an error)

    Error: A JNI error has occurred, please check your installation and try again
    Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value
    3405691578 in class
    Copy the code
  • Prepare: Allocates memory for the class’s static variables and initializes them to default values. This phase simply allocates memory for a static class variable (that is, a static decorated field variable) and sets the initial value for that variable. Public static int value = 123; public static int value = 123; The initial value of the variable value after the preparation phase is 0, not 123, because no Java methods have yet been executed, so assigning value to 123 will be performed during the initialization phase). For variables modified by final static, the memory of the instance variable is allocated at compile time.

  • Parse: converts symbolic references in a class to direct references. A symbolic reference is a set of symbols describing the target, while a direct reference is either a pointer, a relative offset, or a handle that indirectly locates the target.

Class initialization

There are two ways to specify initial values for class variables in a Java class:

  1. Specify an initial value when declaring a class variable
  2. Use static initializer blocks to specify initial values for class variables

The JVM initializing a class consists of the following steps:

  1. If the class has not already been loaded and connected, the program loads and connects the class first.
  2. If the class’s immediate parent has not already been initialized, its immediate parent class is initialized.
  3. If there are initialization statements in the class, the system executes them in turn.

The system also follows steps 1 to 3 to initialize the direct parent class. If the direct parent has a direct parent, the system repeats the three steps to initialize the parent first. So the JVM always initializes java.lang.Object first;

The timing of class initialization

The system initializes a class or interface when a Java program first uses it in one of the following six ways:

  • Create an instance of the class. Creating an instance of a class can be done by using the new operator, reflection, deserialization, etc.
  • Calling class methods (static methods) of a class;
  • Accessing or assigning values to class variables of a class or interface;
  • Use reflection to force the creation of a Java.lang. Class object for a Class or interface.
  • Initializes a subclass of a class. When a subclass of a class is initialized, all the superclasses of that subclass are initialized.
  • Run a main class directly using the java.exe command. When running a main class, the program first initializes the main class;

For a class variable of final type, if the value of the class variable can be determined at compile time, the class variable is equivalent to a “macro variable” (constant). The Java compiler will directly replace the class variable with its value at compile time where it appears;

Conversely, if the value of a final modified class variable cannot be determined at compile time, the value of the class variable must be determined at run time, and if the class variable is accessed through the class, the class will be initialized.

When a class is loaded using the loadClass() method of the ClassLoader class, the method only loads the class and does not initialize the class. Using the Class’s forName() static method causes the Class to be forcibly initialized;

Introduction to class loaders

When the JVM starts, an initialization class loader hierarchy is formed consisting of three class loaders:

  • Bootstrap ClassLoader: the root ClassLoader (not implemented by Java) is responsible for loading the core Java classes (%JAVA_HOME%/jre/lib/rt.jar).

    	public static void main(String[] args) throws Exception {
    		ClassLoader cl = Object.class.getClassLoader();
    	    System.out.println(cl);// The root classloader prints null
    	}
    Copy the code
  • Extension ClassLoader: the Extension ClassLoader is responsible for loading JAR packages in the Extension directory (%JAVA_HOME%/ JRE /lib/ext) of the JRE.

  • System ClassLoader: System ClassLoader that is responsible for loading JAR packages and classpath specified by the -classpath option of the Java command, the java.class.path System property, or the CLASspath environment variable at JVM startup;

    public class ClassLoaderDemo {
        public static void main(String[] args) {
            // Class loaders used by self-written classes
            ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
            System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader}}Copy the code

The system classloader is an instance of AppClassLoader, and the extension classloader is an instance of ExtClassLoader. Both classes are instances of the URLClassLoader class;

Class loading mechanism

The JVM has three main class loading mechanisms:

  • Overall responsibility: When a classloader is responsible for loading a Class, other classes that that Class depends on and references are also responsible for loading by that Class loader, unless explicitly loaded by another classloader.
  • Parent delegate: Let the parent Class loader attempt to load the Class, and only attempt to load the Class from its own classpath if the parent Class loader is unable to do so;
  • Caching mechanism: The caching mechanism will ensure that all the loaded classes will be cached. When a Class is needed in the program, the Class loader will first search for the Class in the cache. Only when the Class object does not exist in the cache, the system will read the binary data corresponding to the Class, convert it into a Class object, and store it in the cache. This is why the JVM must be restarted after Class changes are made for the changes to take effect;

public class ClassLoaderDemo1 {
    public static void main(String[] args) throws Exception{
        // Demonstrate the parent-child relationship of class loaders
        ClassLoader loader = ClassLoaderDemo1.class.getClassLoader();
        while(loader! =null){
            System.out.println(loader);
            loader = loader.getParent();
        }
    }
}
运行结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
Copy the code

All class loaders (except the root ClassLoader) must inherit from java.lang.classloader!

Class loader loading Class step:

  1. Checks whether the Class has been loaded (that is, whether it exists in the cache). If so, go to step 8; otherwise, go to Step 2.

  2. If the parent class loader does not exist (if there is no parent class loader, then either the parent must be the root class loader or is itself the root class loader), then skip to step 4. If the parent class loader exists, then step 3 is performed;

  3. Request to use the parent class loader to load the target class. If successful, skip to step 8, otherwise proceed to step 5.

  4. Request to use the root class loader to load the target class, skip to step 8 if successful, otherwise skip to step 7;

  5. The current ClassLoader tries to find the Class file (from the classpath associated with this ClassLoader). If it finds the Class file, it goes to Step 6. If it cannot find the Class file, it jumps to Step 7.

  6. Load the Class from the file. After successful loading, skip to Step 8.

  7. Throws a ClassNotFoundException;

  8. Return the corresponding Java.lang. Class object;

Benefits of using parent delegation:

  • You can avoid repeated loading of classes. When the parent ClassLoader has already loaded the class, there is no need for the child ClassLoader to load it again
  • For security reasons, the types defined in the Java core API are not randomly replaced. Suppose a class named Java.lang. Object is passed over the network through parent delegate mode to the bootstrap classloader, and the bootstrap classloader finds the class named java.lang.Object in the core Java API and finds that it has been loaded. Instead of reloading the java.lang.Object passed by the network, it returns the loaded object.class, thus preventing the core API library from being tampered with

URLClassLoader

In the java.net package, the JDK provides a much easier ClassLoader, URLClassLoader, which extends the ClassLoader to load classes from a specified location locally or on the network. We can use this class as a custom class loader.

Construction method:

public URLClassLoader(URL[] urls): Specifies the URL address of the class to be loaded. The default parent class loader is the system class loader.public URLClassLoader(URL[] urls, ClassLoader parent): Specifies the URL address of the class to be loaded, and specifies the parent class loader.Copy the code

Case 1: Load classes on disk

public static void main(String[] args) throws Exception{
		File file = new File("d:/");
		URI uri = file.toURI();
		URL url = uri.toURL();
        URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
        System.out.println(classLoader.getParent());
        Class aClass = classLoader.loadClass("com.itheima.Demo");
        Object obj = aClass.newInstance();
    }
Copy the code

Case 2: Loading a class on the network

public static void main(String[] args) throws Exception{
		URL url = new URL("http://localhost:8080/examples/");
        URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
        System.out.println(classLoader.getParent());
        Class aClass = classLoader.loadClass("com.itheima.Demo");
        aClass.newInstance();
}
Copy the code

Custom class loaders

If we need a custom ClassLoader, we just need to extend the ClassLoader class and override the findClass method!

D disk file:Test. Java:

public class Test {
    public  void test(a) {
    	System.out.println("TestAAAAAAAAAAAA"); }}Copy the code

Custom class loaders:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyFileClassLoader extends ClassLoader{
    private String directory;// The directory where the loaded class resides

    /** * specifies the file directory where the class to be loaded is located *@param directory
     */
    public MyFileClassLoader(String directory,ClassLoader parent){
        super(parent);
        this.directory = directory;
    }
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        try {
            // Convert the class name to a directory
            String file = directory+File.separator+name.replace(".", File.separator)+".class";
            // Build the input stream
            InputStream in = new FileInputStream(file);
            // Store the bytes read
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte buf[] = new byte[1024];
            int len = -1;
            while((len=in.read(buf))! = -1){
                baos.write(buf,0,len);
            }
            byte data[] = baos.toByteArray();
            in.close();
            baos.close();
            return defineClass(name,data,0,data.length);
        } catch (IOException e) {
            throw newRuntimeException(e); }}public static void main(String[] args) throws Exception {
        MyFileClassLoader myFileClassLoader = new MyFileClassLoader("d:/".null);
        Class clazz = myFileClassLoader.loadClass("Test");
        clazz.getMethod("test").invoke(clazz.newInstance()); }}// Console print: testaaaaaaaaaaaaaa
Copy the code

Use reflection to view type information

Many objects in a Java program come in two types at runtime:

  • Compile time type
  • Runtime type

Persion p = new Student(); This line of code will generate a p variable whose compile time type is Persion and run time type is Student.

In some cases, programs need to discover real information about objects and classes at runtime. There are two ways to solve this problem:

  • The first is to assume that the type is fully known at compile time and at run time. In this case, you can use the instanceof operator to determine the type and then cast it to a variable of its run time type.
  • Second, there is no way to know at compile time what classes the object and class might belong to. The program only relies on runtime information to find out the true information about the object and class, which requires reflection.

Getting a Class object

Using reflection mechanism, you can dynamically obtain the information of the current class, such as method information, annotation information, method parameters, properties, etc.

public class User {
    private String name;
    private Integer age;

    public User(String name, Integer age) {
        System.out.println("I have a constructor with parameters.");
        this.name = name;
        this.age = age;
    }

    public User(a){
        System.out.println("I'm a no-argument constructor.");
    }
    
    private void UserPrivateMethod(a){
        System.out.println("I'm User's private method."); }}Copy the code

There are three common ways to obtain a Class object in a Java program:

  • Use the forName(String clazzName) static method of Class. This method needs to pass a string argument with the value being the fully qualified class name of a class (the full package name must be added);

    Class<? > aClass = Class.forName("com.example.demo.entity.User");
    Copy the code
  • Call the class attribute of a class to get the corresponding class object for that class.

    Class<User> aClass = User.class;
    Copy the code
  • Call the getClass() method on an object. This method is a method of the Java.lang. Object Class, so all Java objects can call this method. This method will return the Class Object of the Class to which the Object belongs.

    User user = new User();
    Class<? extends User> aClass = user.getClass();
    Copy the code

1. Initialize the object with no-argument constructs

Class<? > aClass = Class.forName("com.example.demo.entity.User");

//newInstance() executes the no-argument constructor. Most frameworks use newInstance() to initialize an object. This is why we report an error when our object is not constructed without arguments
User user = (User) aClass.newInstance();
user.setName("甲");
user.setAge(12);
System.out.println(user);

// Console printI'm the no-argument constructor User{name='甲', age=12}
Copy the code

2. Initialize an object using a constructor with parameters

Class<? > aClass = Class.forName("com.example.demo.entity.User");
// Execute the constructor with argumentsConstructor<? > constructor = aClass.getConstructor(String.class, Integer.class); User user2 = (User) constructor.newInstance("乙".24);
System.out.println(user2);

// Console printI do take a constructor User{name='乙', age=24}
Copy the code

3. Assign values to private attributes

Class<? > aClass = Class.forName("com.example.demo.entity.User");
User user1 = (User) aClass.newInstance();
Field name = aClass.getDeclaredField("name");
Field age = aClass.getDeclaredField("age");

Class XXX can not access a member of Class XXX with modifiers "private"
name.setAccessible(true);
age.setAccessible(true);
name.set(user1,"丙");
age.set(user1,36);
System.out.println(user1);
Copy the code

4. Use reflection to invoke methods

Class<? > aClass = Class.forName("com.example.demo.entity.User");
User user = (User) aClass.newInstance();
Method method = aClass.getDeclaredMethod("UserPrivateMethod".null);
method.setAccessible(true);
Object invoke = method.invoke(user, null);
System.out.println(invoke);
Copy the code

Explicit and implicit loading of class

The class loading mode refers to how the VM loads the class file to the memory.

Explicit loading refers to loading a class object in Java code by calling the ClassLoader. For example:

  • Class.forname (String name);
  • This.getclass ().getClassLoader().loadClass() load the class;

Implicit loading means that the loaded code does not need to be explicitly called in Java code, but is automatically loaded into memory via the virtual machine. For example, when a class is loaded that references an object of another class, the bytecode file of that object is automatically loaded into memory by the virtual machine.

Books: Crazy Java handout study notes, hereby recorded