Java provides a reflection mechanism that allows you to examine class information at run time
Java class loading
Java loads a class when it really needs to be used, rather than loading all classes at startup. Since most users only use part of the program’s resources, loading some resources when they need some functionality makes system resources more efficient.
Loading a class involves reading binary data from a class’s.class file into memory, placing it in the Jvm’s method area, and then creating a Java.lang. class object in the heap that encapsulates the class’s data structure in the method area. The end product of Class loading is the Class object in the heap, which encapsulates the Class’s data structure in the method area and provides the Java programmer with an interface to access the data structure in the method area.
All types in Java include primitive types (
int
.
long
.
float
And so on), even arrays have objects of Class Class associated with them.
Class objects are automatically generated by the Jvm, which automatically generates a Class object for a Class whenever it is loaded
Class object
Instance. GetClass ()
Get the corresponding Class Object for each instance via Object’s getClass()
String name = "hello";
Class stringClass = name.getClass();
System.out.println("Class name :" + stringClass.getName());
System.out.println("Interface or not :" + stringClass.isInterface());
System.out.println("Basic type or not :" + stringClass.isPrimitive());
System.out.println("Array or not :" + stringClass.isArray());
System.out.println("Parent name :" + stringClass.getSuperclass().getName());
Copy the code
The name of the class:java.lang.String
Interface or not:false
Whether it is a basic type:false
Array or not:false
Name of the parent class:java.lang.Object
Copy the code
The name of the class. The class
You can also get the String Class object directly using the following method
Class stringClass = String.class;
Copy the code
Class.forName()
In some applications, you can’t know in advance what classes the user will load. You can use Class’s static forName() method to load classes dynamically
Class c = Class.forName(args[0]);
System.out.println("Class name :" + c.getName());
System.out.println("Interface or not :" + c.isInterface());
System.out.println("Basic type or not :" + c.isPrimitive());
System.out.println("Array or not :" + c.isArray());
System.out.println("Parent name :" + c.getSuperclass().getName());
Copy the code
$ java Demo1 java.util.Scanner
The name of the class:java.util.Scanner
Interface or not:false
Whether it is a basic type:false
Array or not:false
Name of the parent class:java.lang.Object
Copy the code
There are two versions of class.forname (). The first version specifies only the fully qualified Class name, while the other version lets you specify the Class name, whether to execute a static code block at load time, and execute a ClassLoader.
static Class forName(String name, boolean initialize, ClassLoader loader)
Copy the code
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// The class.forname () load Class executes the initialization block by default
Class.forName("Test2");
// class.forname () loads the Class. The second argument controls whether the initialization block is executed
Class.forName("Test2".false, loader);
class Test2 {
static {
System.out.println("Static initialization block executed!");
}
}
Copy the code
Get information from the Class object
The Class object represents the Class being loaded. Once you get the Class object, you can get information about the Class, including package, constructor, field, method, etc. And for every kind of information, there is a corresponding category
- package: java.lang.reflect.Package
- constructor: java.lang.reflect.Constructor
- field: java.lang.reflect.Field
- method: java.lang.reflect.Method
Class c = Class.forName(args[0]);
System.out.println("Package information package:" + c.getPackage());
System.out.println("Class modifier modifier:" + c.getModifiers());
System.out.println("Constructor :");
Arrays.stream(c.getDeclaredConstructors()).forEach(System.out::println);
System.out.println("Member variable fields:");
Arrays.stream(c.getDeclaredFields()).forEach(System.out::println);
Copy the code
$ java Demo1 java.util.ArrayList
Package information Package: Package java.util
Class modifier modifier:1
A constructorconstructor:
public java.util.ArrayList(java.util.Collection)
public java.util.ArrayList()
public java.util.ArrayList(int)
Member variable Fields:
private static final long java.util.ArrayList.serialVersionUID
private static final int java.util.ArrayList.DEFAULT_CAPACITY
private static final java.lang.Object[] java.util.ArrayList.EMPTY_ELEMENTDATA
private static final java.lang.Object[] java.util.ArrayList.DEFAULTCAPACITY_EMPTY_ELEMENTDATA
transient java.lang.Object[] java.util.ArrayList.elementData
private int java.util.ArrayList.size
private static final int java.util.ArrayList.MAX_ARRAY_SIZE
Copy the code
ClassLoader ClassLoader
Java loads classes only when they are needed. In Java, classes are loaded by the Class Loader.
When you try to execute the Java XXX command, Java will try to find the JRE installation directory, look for jVM.dll, and start the JVM and initialize it. Then produce BootstrapLoader, the Bootstrap Loader will load the Extended Loader, and set the Extended the parent for BootstrapLoader Loader, The Bootstrap Loader then loads the Application Loader and sets the parent of the Application Loader to Extended Loader
Start the class loader
BootstrapLoader searches for classes specified in sun.boot.library.path, which you can obtain using System.getProperty(“sun.boot.library.path”)
/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib
Copy the code
Extend the class loader
The Extended Loader(Sun.misc.launcher $ExtClassLoader) is written in Java and searches for the category specified in the system parameter java.ext.dirs, This can be obtained via System.getProperty(“java.ext.dirs”)
/Users/dsying/Library/Java/Extensions:
/Library/Java/JavaVirtualMachines/jdk18.. 0_151.jdk/Contents/Home/jre/lib/ext:
/Library/Java/Extensions:
/Network/Library/Java/Extensions:
/System/Library/Java/Extensions:/usr/lib/java
Copy the code
Application class loader
The Application Loader (sun.misc.Launcher$AppClassLoader) is written in Java and searches for the category specified in the system parameter java.class.path. This can be obtained via System.getProperty(“java.class.path”), and the classpath can be set with the -cp parameter when executing the. Class bytecode file using the Java XXX command
Java - cp. / classes SomeClass
Copy the code
Relationships between class loaders
ClassLoader loader Thread.currentThread().getContextClassLoader();
// sun.misc.Launcher$AppClassLoader@18b4aac2 Application class loader
System.out.println(loader);
// sun.misc.Launcher$ExtClassLoader@610455d6 Extension class loader
System.out.println(loader.getParent());
// Bootstrap ClassLoader starts the ClassLoader (C)
System.out.println(loader.getParent().getParent());
Copy the code
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@610455d6
null
Copy the code
There are three ways to load a class:
- The command line startup application is initialized by the JVM
- through
Class.forName()
Method dynamic loading - through
ClassLoader.loadClass()
Method dynamic loading
Class.forname () and classLoader.loadClass ()
Class.forName()
In addition to loading the class’s.class file into the JVM, the class is interpreted, executing the static block of the class;ClassLoader.loadClass()
Class files are loaded into the JVM. Static blocks are executed only at newInstance.Class.forName(name, initialize, loader)
Parameterized functions can also control whether static blocks are loaded. And only the newInstance() method is invoked to create objects of the class using the call constructor.
JVM class loading mechanism
Overall responsible for
When a Class is loaded by a Class loader, other classes that the Class depends on and references are also loaded by the Class loader, unless it is shown to be loaded using another Class loaderCommissioned by the parent class
, first let the parent class loader try to load the class, and only try to load the class from its own classpath if the parent class loader cannot load the classCaching mechanisms
When a program needs to use a Class, the Class loader first looks for the Class in the cache. Only when the cache does not exist, the system will read the binary data corresponding to the Class, convert it into a Class object, and store it in the cache. Is that why you have to restart the JVM to make Class changes to take effect
Parental delegation model
The workflow of the parental delegation model is as follows: If a class loader received the request of the class loading, it won’t try to load the first class, but give parent to complete the request, in turn up, as a result, all of the class loading request should be passed to the top finally start the class loader, only when the parent loader in the scope of its search did not find the required class, is unable to complete the loading, The child loader will try to load the class itself.
Parent delegate mechanism:
- when
AppClassLoader
When a class is loaded, it first does not attempt to load the class itself. Instead, it delegates the class-loading request to the parent classloaderExtClassLoader
To complete. - when
ExtClassLoader
When a class is loaded, it doesn’t try to load the class itself in the first place. Instead, it delegates the class-loading request toBootStrapClassLoader
To complete. - if
BootStrapClassLoader
Loading failed (for example in$JAVA_HOME/jre/lib
Class was not found inExtClassLoader
To try to load; - if
ExtClassLoader
Also failed to load, will be usedAppClassLoader
To load, ifAppClassLoader
If the load fails, an exception is reportedClassNotFoundException
.
ClassLoader source code:
publicClass<? > loadClass(String name)throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronizedClass<? > loadClass(String name,boolean resolve)throws ClassNotFoundException {
// First check if the type is already loaded
Class c = findLoadedClass(name);
if (c == null) {
// If it is not loaded, delegate to the parent class loader or delegate to the start class loader
try {
if(parent ! =null) {
// Delegate to the parent class loader if there is one
c = parent.loadClass(name, false);
} else {
// If there is no parent Class loader, check whether the Class was loaded by the bootloader, by calling the native Class findBootstrapClass(String name).
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// Call its own loading function if neither the parent class loader nor the launcher class loader can complete the loading task
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
Copy the code
Significance of parental delegation model:
- System classes prevent multiple copies of the same bytecode from appearing in memory
- Ensure the safe and stable running of Java programs
Custom loader
Custom classloaders are generally inherited from classloaders, so we just need to override the findClass method. Here’s an example to illustrate the process of customizing a class loader:
package com.github.hcsp.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
// Directory where the bytecode files are stored
private final File bytecodeFileDirectory;
public MyClassLoader(File bytecodeFileDirectory) {
this.bytecodeFileDirectory = bytecodeFileDirectory;
}
// Remember what class loaders do?
// "Load a Class definition (Class object) from an external system"
// Implement a custom Class loader that loads the bytecode files in the current directory as Class objects
// Note that, in general, to implement a custom classloader, you need to override the following methods:
//
// 1. If the bytecode file corresponding to the class name exists, it is read as a byte array
// 1.1 Calls classLoader.defineclass () to convert the byte array to a Class object
// 2. If the bytecode file corresponding to the class name does not exist, throw ClassNotFoundException
//
@Override
protectedClass<? > findClass(String name)throws ClassNotFoundException {
byte[] classData = getByteArrayFromFile(name);
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
}
byte[] getByteArrayFromFile(String className) throws ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
File file = new File(bytecodeFileDirectory, className + ".class");
int len = 0;
try {
byte[] bufferSize = new byte[1024];
FileInputStream fis = new FileInputStream(file);
while((len = fis.read(bufferSize)) ! = -1) {
bos.write(bufferSize, 0, len);
}
} catch (FileNotFoundException e) {
throw new ClassNotFoundException();
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}
public static void main(String[] args) throws Exception {
File projectRoot = new File(System.getProperty("basedir", System.getProperty("user.dir")));
MyClassLoader myClassLoader = new MyClassLoader(projectRoot);
Class testClass = myClassLoader.loadClass("com.github.hcsp.MyTestClass");
Object testClassInstance = testClass.getConstructor().newInstance();
String message = (String) testClass.getMethod("sayHello").invoke(testClassInstance);
System.out.println(message);
}
}
Copy the code
The core of the custom class loader is the retrieval of the bytecode file, which needs to be decrypted in the class if it is encrypted bytecode. Since this is just a demonstration, I have not encrypted the class file, so there is no decryption.
other
Use reflection to create objects
You can instantiate this using the newInstance() method of Class
Class c = Class.forName(className);
Object obj = c.newInstance();
Copy the code
A method is called
In the class using reflection can retrieve Method, the corresponding class for Java. Lang. Reflect. The Method, you can use it to invoke () Method to invoke the specified Method
Class testClass = myClassLoader.loadClass("com.github.hcsp.MyTestClass");
Object testClassInstance = testClass.getConstructor().newInstance();
String message = (String) testClass.getMethod("sayHello").invoke(testClassInstance);
Copy the code