When asked about the class loading mechanism, most of you can answer the question about the parent delegate mechanism, and also know that for security reasons, the JVM does not load strings with the same fully qualified class name.

But what happens if it is loaded? Exception? What an anomaly that is. What if the package name is different? Can custom class loaders be loaded?

I believe that the interviewer will attack from a variety of angles, and will soon answer the bug, after all, I haven’t studied the virtual machine deeply…

Next, the author will verify the above problems one by one. This article starts from the direction of proving the answer without much theoretical explanation. If there is an understanding of the deviation, but also hope that you do not hesitate to give advice.

What class loaders the JVM has

Let’s start with an excerpt from the network of JVM classloading mechanisms

There are three important classloaders built into the JVM, all of which are implemented by Java and inherit from java.lang.ClassLoader except BootstrapClassLoader:

  • BootstrapClassLoader: the topmost loading class, implemented by C++, that loads jar packages and classes in the %JAVA_HOME%/lib directory or all classes in the path specified by the -xbootclasspath parameter.

  • ExtensionClassLoader: load jar packages and classes in %JRE_HOME%/lib/ext or jar packages in the path specified by the java.ext.dirs system variable.

  • AppClassLoader: a loader for our users that loads all jar packages and classes in the current application classpath.

JVM class loading

There are three ways to load a class:

1. The command line startup application is initialized by the JVM

2. Load dynamically through class.forname ()

3. Use the classloader.loadClass () method to load dynamically

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 () : does only one thing: loads a. Class file into the JVM. Static blocks are executed only at newInstance.
  • Class. ForName (name,initialize,loader) functions with parameters 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

Full responsibility. When a Class loader is responsible for loading a Class, other classes that that Class depends on and references are also loaded by the Class loader, unless it is shown to be loaded using another Class loader.

The parent delegate, which first lets the parent loader try to load the class, and only tries to load the class from its own classpath if the parent loader fails to load the class.

The caching mechanism will ensure that all loaded classes will be cached. 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. This is why after Class changes are made, the JVM must be restarted for the program changes to take effect.

JVM class loading mechanism source

Parental delegation model implements source code analysis

private final ClassLoader parent; protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check whether the requested Class has been loaded Class<? > c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent ! C = parent.loadClass(name, false); C = findBootstrapClassOrNull(name); c = findBootstrapClassOrNull(name); }} Catch (ClassNotFoundException e) {if (c == null) {long t1 = system.nanotime (); C = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}Copy the code

* Benefits of the parent delegate model *

The parent delegate model ensures that Java programs run stably, that classes are not reloaded (the WAY the JVM distinguishes classes is not just based on class names; the same class file is loaded by different classloaders to produce two different classes), and that Java’s core apis are not tampered with.

If instead of using the parent delegate model, each class loader loads itself, there are some problems. For example, if we write a class called java.lang.Object, the system will have multiple Object classes when the program runs.

What if we don’t want to parent the delegate model?

To avoid the parent delegate mechanism, we can define our own class loader and override loadClass().

The system classloader loads a custom String

1. First, let’s look at the normal class loading process

package com.example.demojava.loadclass; public class ClassLoaderDemo{ public static void main(String[] args) { System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader()); System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent()); System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent()); }}Copy the code

Results output

ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@75bd9247
The GrandParent of ClassLodarDemo's ClassLoader is nullCopy the code

The parent class loader of AppClassLoader is ExtClassLoader. The parent class loader of ExtClassLoader is null. Null does not mean that ExtClassLoader does not have a parent class loader, but BootstrapClassLoader.

2. Let’s define a String class and see what happens

package com.example.demojava.loadclass; Public class String {public static void main(String[] args) {system.out.println (" I am a custom String"); }}Copy the code

Results output

➜ demo - Java javac SRC/main/Java/com/example/demojava/loadclass/String Java ➜ demo - Java Java Src.main.java.com.example.demojava.loadclass.String error: cannot find or unable to load the main class src.main.java.com.example.demojava.loadclass.StringCopy the code

The class name is not the same as the JDK String package. The main class cannot be found or loaded.

The AppClassLoader does not import a custom String from the AppClassLoader. The AppClassLoader does not import a custom String from the AppClassLoader. The main() method cannot be found because the String object is a custom object that does not conform to the main() method definition.

So let’s go back and verify that, what happens if I run ClassLoaderDemo again? What? What happened to the main() method in the IDE? Let’s just compile and run it manually

➜ demo - Java javac SRC/main/Java/com/example/demojava/loadclass/ClassLoaderDemo Java ➜ demo - Java Java Src.main.java.com.example.demojava.loadclass.ClassLoaderDemo error: Can't find or unable to load the main class src.main.java.com.example.demojava.loadclass.ClassLoaderDemoCopy the code

The result: The previously healthy Java class can no longer find the main class.

Let’s import the correct String class to verify

package com.example.demojava.loadclass; Public class String {public static void main(java.lang.string [] args) {system.out.println (" I am a custom String"); }}Copy the code

Results output

I'm a custom StringCopy the code

3. Can you overwrite the String class under the lang package?

Change the package path in the example above

package java.lang; Public class String {public static void main(java.lang.string [] args) {system.out.println (" I am a custom String"); }}Copy the code

The output error

Connected to the target VM, address: '127.0.0.1:63569', transport: 'socket' The main method is not found in the java.lang.String class. Define the main method as: Public static void main (String [] args) or deployment headaches the application class must extend deployment headaches. The application. The applicationCopy the code

Analysis:

Java.lang. String = java.lang.String; java.lang.String = java.lang.String; java.lang.String = java.lang.String; Java.lang.string does not have a main() method, so an error is reported. It’s a safety mechanism.

Then verify that the default class loader can load the custom java.lang.String. The default AppClassLoader can load Everything?

public class LoadStringDemo { public static void main(String[] args) { URLClassLoader systemClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader(); URL[] urLs = systemClassLoader.getURLs(); for (URL url: urLs) { System.out.println(url); }}}Copy the code

The following log output is displayed:

. file:/Users/cuishiying/work/demo-java/target/classes/ ...Copy the code

Too many logs, but absolutely no other package path (java.lang.String under the current package can only be in JDK by default)

Custom class loaders

Why does a custom class loader exist

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.

Because in a real project, there are many ways to load a.class file,

  • Load directly from the local system
  • Download the. Class file from the network
  • Load. Class files from zip, JAR, etc archives
  • Extract. Class files from a proprietary database
  • Dynamically compile Java source files into.class files

How do I customize class loaders

package com.example.demojava.loadclass; import com.demo.ClassLoaderDemo; import java.io.*; import java.lang.reflect.Method; public class MyClassLoader extends ClassLoader { private String root; /** * @param name Fully qualified Class name * @return * @throws ClassNotFoundException */ @override protected Class<? > findClass(String name) throws ClassNotFoundException { byte[] classData = loadClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] loadClassData(String className) { String fileName = root + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; try { InputStream ins = new FileInputStream(fileName); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int length = 0; while ((length = ins.read(buffer)) ! = -1) { baos.write(buffer, 0, length); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } public String getRoot() { return root; } public void setRoot(String root) { this.root = root; } public static void main(String[] args) throws Exception { MyClassLoader classLoader = new MyClassLoader(); classLoader.setRoot("/Users/cuishiying/Desktop/demo"); Class<? > clz = Class.forName("LoadDemo", true, classLoader); Object instance = clz.newInstance(); Method test = clz.getDeclaredMethod("test"); test.setAccessible(true); test.invoke(instance); System.out.println(instance.getClass().getClassLoader()); }}Copy the code

Results output

test
com.example.demojava.loadclass.MyClassLoader@75bd9247Copy the code

As you can see, the custom class loader is working. We cannot place LoadDemo under the classpath, because the parent delegate mechanism will directly cause the class to be loaded by the AppClassLoader instead of our custom class loader.

Custom class loaders load handwritten java.lang.String

Overrides the main() method of the custom classloader

public static void main(String[] args) throws Exception { MyClassLoader classLoader = new MyClassLoader(); classLoader.setRoot("/Users/cuishiying/Desktop/demo"); Class<? > clz = classLoader.findClass("java.lang.String"); Object instance = clz.newInstance(); System.out.println(instance.getClass().getClassLoader()); }Copy the code

The JVM threw a SecurityException due to a security mechanism

/Users/cuishiying/Desktop/demo/java/lang/String.class
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
    at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at com.example.demojava.loadclass.MyClassLoader.findClass(MyClassLoader.java:25)
    at com.example.demojava.loadclass.MyClassLoader.main(MyClassLoader.java:71)Copy the code

Wenyuan network, only for the use of learning, such as infringement, contact deletion.

I’ve compiled the interview questions and answers in PDF files, as well as a set of learning materials covering, but not limited to, the Java Virtual Machine, the Spring framework, Java threads, data structures, design patterns and more.

Follow the public account “Java Circle” for information, as well as quality articles delivered daily.