Home Java JavaBase Detailed explanation of java class loader ClassLoader

Detailed explanation of java class loader ClassLoader

Nov 27, 2019 pm 03:35 PM
classloader class loader

Detailed explanation of java class loader ClassLoader

Ways to get ClassLoader

1. Get the ClassLoader of the current class

clazz.getClassLoader()
Copy after login

2. Get the ClassLoader of the current thread context

Thread.currentThread().getContextClassLoader();
Copy after login

3. Get the system’s ClassLoader

ClassLoader.getSystemClassLoader()
Copy after login

4. Get the caller’s ClassLoader

DriverManager.getCallerClassLoader
Copy after login

ClassLoader source code analysis

(Recommended learning: Java Video Tutorial)

Overview

The class loader is used to load classes Object, ClassLoader is an abstract class. If we are given the binary name of a class, the class loader should try to locate or generate the data that makes up the defining class. A typical strategy is to convert a given binary name into a file name, and then read the class file corresponding to this file name from the file system.

Each Class object will contain a reference to the ClassLoader that defines it.

The Class object of the array class is not created by the class loader, but is automatically created by the JVM as needed during the Java runtime. For the class loader of the array class, it is returned through Class.getClassLoader(), which is the same as the class loader of the element type in the array; if the element type in the array is a native type, The array class does not have a class loader [Code 1].

The application implements a subclass of ClassLoader to extend the way the JVM dynamically loads classes.

Class loaders are typically used by security managers to identify security domain issues.

The ClassLoader class uses a delegation model to find classes and resources. Each instance of ClassLoader will have a parent ClassLoader associated with it. When ClassLoader is asked to find a class or resource, the ClassLoader instance tries on its own Before searching for a class or resource, its parent class loader will be delegated to complete it. The built-in class loader of the virtual machine, called the startup class loader, does not have a parent loader, but it can be used as the parent class loader of a class loader [Parental delegation mechanism].

A class loader that supports concurrent class loading is called a parallel class loader. It is required to register itself through the ClassLoader.registerAsParallelCapable method during initialization. The ClassLoader class is registered as parallel by default, but if it If the subclasses are also loaded in parallel, you need to register the subclasses separately.

In an environment where the delegation model is not strictly hierarchical, the class loader needs to be parallel, otherwise class loading will cause deadlock, because the loader's lock is always held during the class loading process.

Normally, the Java virtual machine loads classes from the local file system in a platform-dependent manner. For example, in UNIX systems, the virtual machine loads classes from the directory defined by the CLASSPATH environment.
However, some classes do not come from files; they are obtained from other sources, such as the network, or [dynamic proxies] are built by the application itself. The defineClass(defineClass) method will convert the byte array into an instance of Class. An instance of this newly defined class can be created by Class.newInstance.

The methods and constructors of objects created by a class loader may reference other classes. In order to determine the referenced class, the Java virtual machine calls the loadClass of the class loader that originally created the class. method.

Binary name: Any class name provided to CalssLoader in the form of a string parameter must be a binary name, including the following four situations

  • "java.lang. String" Normal class
  • "javax.swing.JSpinner$DefaultEditor" Internal class
  • "java.security.KeyStore\(Builder\)FileBuilder$1" Internal of KeyStore The inner class of class Builder The first anonymous inner class of FileBuilder
  • "java.net.URLClassLoader$3$1" The third anonymous inner class of URLClassLoader class The first anonymous inner class

Code 1:

public class Test12 {
    public static void main(String[] args) {
        String[] strings = new String[6];
        System.out.println(strings.getClass().getClassLoader());
        // 运行结果:null

        Test12[] test12s = new Test12[1];
        System.out.println(test12s.getClass().getClassLoader());
        // 运行结果:sun.misc.Launcher$AppClassLoader@18b4aac2

        int[] ints = new int[2];
        System.out.println(ints.getClass().getClassLoader());
        // 运行结果:null
    }
}
Copy after login

loadClass method

The source code of loadClass is as follows. The loadClass method loads the Class with the specified binary name. By default, it is in the following order. Find a class:

a) Call findLoadedClass(String) to check whether this class is loaded

b) Call the loadClass method of the parent class loader. If the parent class loader is null, it will be called Start the class loader

c) Call the findClass(String) method to find

Use the above steps if the class is found and resolve is true, the resolveClass(Class) method will be called

protected Class<?> loadClass(String name, boolean resolve)
  throws ClassNotFoundException
{
  synchronized (getClassLoadingLock(name)) {
      // First, check if the class has already been loaded
      Class<?> c = findLoadedClass(name);
      if (c == null) {
          long t0 = System.nanoTime();
          try {
              if (parent != null) {
                  c = parent.loadClass(name, false);
              } else {
                  c = findBootstrapClassOrNull(name);
              }
          } catch (ClassNotFoundException e) {
              // ClassNotFoundException thrown if class not found
              // from the non-null parent class loader
          }

          if (c == null) {
              // If still not found, then invoke findClass in order
              // to find the class.
              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 after login

findClass method

findClass的源码如下,findClass寻找拥有指定二进制名称的类,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass方法调用,默认返回ClassNotFoundException异常。

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}
Copy after login

defineClass方法

defineClass的源码如下,defineClass方法将一个字节数组转换为Class的实例。

protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                     ProtectionDomain protectionDomain)
    throws ClassFormatError
{
    protectionDomain = preDefineClass(name, protectionDomain);
    String source = defineClassSourceLocation(protectionDomain);
    Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
    postDefineClass(c, protectionDomain);
    return c;
}
Copy after login

自定义类加载器

/**
 * 继承了ClassLoader,这是一个自定义的类加载器
 * @author 夜的那种黑丶
 */
public class ClassLoaderTest extends ClassLoader {
    public static void main(String[] args) throws Exception {
        ClassLoaderTest loader = new ClassLoaderTest("loader");
       Class<?> clazz = loader.loadClass("classloader.Test01");
        Object object = clazz.newInstance();
        System.out.println(object);
        System.out.println(object.getClass().getClassLoader());
    }
    //------------------------------以上为测试代码---------------------------------

    /**
     * 类加载器名称,标识作用
     */
    private String classLoaderName;

    /**
     * 从磁盘读物字节码文件的扩展名
     */
    private String fileExtension = ".class";

    /**
     * 创建一个类加载器对象,将系统类加载器当做该类加载器的父加载器
     * @param classLoaderName 类加载器名称
     */
    private ClassLoaderTest(String classLoaderName) {
        // 将系统类加载器当做该类加载器的父加载器
        super();
        this.classLoaderName = classLoaderName;
    }

    /**
     * 创建一个类加载器对象,显示指定该类加载器的父加载器
     * 前提是需要有一个类加载器作为父加载器
     * @param parent 父加载器
     * @param classLoaderName 类加载器名称
     */
    private ClassLoaderTest(ClassLoader parent, String classLoaderName) {
        // 显示指定该类加载器的父加载器
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    /**
     * 寻找拥有指定二进制名称的类,重写ClassLoader类的同名方法,需要自定义加载器遵循双亲委托机制
     * 该方法会在检查完父类加载器之后被loadClass方法调用
     * 默认返回ClassNotFoundException异常
     * @param className 类名
     * @return Class的实例
     * @throws ClassNotFoundException 如果类不能被找到,抛出此异常
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        byte[] data = this.loadClassData(className);
        /*
         * 通过defineClass方法将字节数组转换为Class
         * defineClass:将一个字节数组转换为Class的实例,在使用这个Class之前必须要被解析
         */
        return this.defineClass(className, data, 0 , data.length);
    }

    /**
     * io操作,根据类名找到对应文件,返回class文件的二进制信息
     * @param className 类名
     * @return class文件的二进制信息
     * @throws ClassNotFoundException 如果类不能被找到,抛出此异常
     */
    private byte[] loadClassData(String className) throws ClassNotFoundException {
        InputStream inputStream = null;
        byte[] data;
        ByteArrayOutputStream byteArrayOutputStream = null;

        try {
            this.classLoaderName = this.classLoaderName.replace(".", "/");
            inputStream = new FileInputStream(new File(className + this.fileExtension));
            byteArrayOutputStream = new ByteArrayOutputStream();

            int ch;
            while (-1 != (ch = inputStream.read())) {
                byteArrayOutputStream.write(ch);
            }

            data = byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            throw new ClassNotFoundException();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }
}
Copy after login

以上是一段自定义类加载器的代码,我们执行这段代码

classloader.Test01@7f31245a
sun.misc.Launcher$AppClassLoader@18b4aac2
Copy after login

可以看见,这段代码中进行类加载的类加载器还是系统类加载器(AppClassLoader)。这是因为jvm的双亲委托机制造成的,private ClassLoaderTest(String classLoaderName)将系统类加载器当做我们自定义类加载器的父加载器,jvm的双亲委托机制使自定义类加载器委托系统类加载器完成加载。

改造以下代码,添加一个path属性用来指定类加载位置:

public class ClassLoaderTest extends ClassLoader {
    public static void main(String[] args) throws Exception {
        ClassLoaderTest loader = new ClassLoaderTest("loader");
        loader.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/");
        Class<?> clazz = loader.loadClass("classloader.Test01");
        System.out.println("class:" + clazz);

        Object object = clazz.newInstance();
        System.out.println(object);
        System.out.println(object.getClass().getClassLoader());
    }
    //------------------------------以上为测试代码---------------------------------

    /**
     * 从指定路径加载
     */
    private String path;

    ......
    
    /**
     * io操作,根据类名找到对应文件,返回class文件的二进制信息
     * @param className 类名
     * @return class文件的二进制信息
     * @throws ClassNotFoundException 如果类不能被找到,抛出此异常
     */
    private byte[] loadClassData(String className) throws ClassNotFoundException {
        InputStream inputStream = null;
        byte[] data;
        ByteArrayOutputStream byteArrayOutputStream = null;

        className = className.replace(".", "/");

        try {
            this.classLoaderName = this.classLoaderName.replace(".", "/");
            inputStream = new FileInputStream(new File(this.path + className + this.fileExtension));
            byteArrayOutputStream = new ByteArrayOutputStream();

            int ch;
            while (-1 != (ch = inputStream.read())) {
                byteArrayOutputStream.write(ch);
            }

            data = byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            throw new ClassNotFoundException();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    public void setPath(String path) {
        this.path = path;
    }
}
Copy after login

运行一下

class:class classloader.Test01
classloader.Test01@7f31245a
sun.misc.Launcher$AppClassLoader@18b4aac2
Copy after login

修改一下测试代码,并删除工程下的Test01.class文件

public static void main(String[] args) throws Exception {
    ClassLoaderTest loader = new ClassLoaderTest("loader");
   loader.setPath("/home/fanxuan/桌面/");
    Class<?> clazz = loader.loadClass("classloader.Test01");
    System.out.println("class:" + clazz);

    Object object = clazz.newInstance();
    System.out.println(object);
    System.out.println(object.getClass().getClassLoader());
}
Copy after login

运行一下

class:class classloader.Test01
classloader.Test01@135fbaa4
classloader.ClassLoaderTest@7f31245a
Copy after login

分析

改造后的两块代码,第一块代码中加载类的是系统类加载器AppClassLoader,第二块代码中加载类的是自定义类加载器ClassLoaderTest。是因为ClassLoaderTest会委托他的父加载器AppClassLoader加载class,第一块代码的path直接是工程下,AppClassLoader可以加载到,而第二块代码的path在桌面目录下,所以AppClassLoader无法加载到,然后ClassLoaderTest自身尝试加载并成功加载到。如果第二块代码工程目录下的Test01.class文件没有被删除,那么依然是AppClassLoader加载。

再来测试一块代码

public static void main(String[] args) throws Exception {
    ClassLoaderTest loader = new ClassLoaderTest("loader");
    loader.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/");
    Class<?> clazz = loader.loadClass("classloader.Test01");
    System.out.println("class:" + clazz.hashCode());

    Object object = clazz.newInstance();
    System.out.println(object.getClass().getClassLoader());

    ClassLoaderTest loader2 = new ClassLoaderTest("loader");
    loader2.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/");
    Class<?> clazz2 = loader2.loadClass("classloader.Test01");
    System.out.println("class:" + clazz2.hashCode());

    Object object2 = clazz2.newInstance();
    System.out.println(object2.getClass().getClassLoader());
}
Copy after login

结果显而易见,类由系统类加载器加载,并且clazz和clazz2是相同的。

class:2133927002
sun.misc.Launcher$AppClassLoader@18b4aac2
class:2133927002
sun.misc.Launcher$AppClassLoader@18b4aac2
Copy after login

再改造一下

public static void main(String[] args) throws Exception {
    ClassLoaderTest loader = new ClassLoaderTest("loader");
    loader.setPath("/home/fanxuan/桌面/");
    Class<?> clazz = loader.loadClass("classloader.Test01");
    System.out.println("class:" + clazz.hashCode());

    Object object = clazz.newInstance();
    System.out.println(object.getClass().getClassLoader());

    ClassLoaderTest loader2 = new ClassLoaderTest("loader2");
    loader2.setPath("/home/fanxuan/桌面/");
    Class<?> clazz2 = loader2.loadClass("classloader.Test01");
    System.out.println("class:" + clazz2.hashCode());

    Object object2 = clazz2.newInstance();
    System.out.println(object2.getClass().getClassLoader());
}
Copy after login

运行结果

class:325040804
classloader.ClassLoaderTest@7f31245a
class:621009875
classloader.ClassLoaderTest@45ee12a7
Copy after login

ClassLoaderTest是显而易见,但是clazz和clazz2是不同的,这是因为类加载器的命名空间的原因。

我们可以通过设置父类加载器来让loader和loader2处于同一命名空间

public static void main(String[] args) throws Exception {
    ClassLoaderTest loader = new ClassLoaderTest("loader");
    loader.setPath("/home/fanxuan/桌面/");
    Class<?> clazz = loader.loadClass("classloader.Test01");
    System.out.println("class:" + clazz.hashCode());

    Object object = clazz.newInstance();
    System.out.println(object.getClass().getClassLoader());

    ClassLoaderTest loader2 = new ClassLoaderTest(loader, "loader2");
    loader2.setPath("/home/fanxuan/桌面/");
    Class<?> clazz2 = loader2.loadClass("classloader.Test01");
    System.out.println("class:" + clazz2.hashCode());

    Object object2 = clazz2.newInstance();
    System.out.println(object2.getClass().getClassLoader());
}
Copy after login

运行结果

class:325040804
classloader.ClassLoaderTest@7f31245a
class:325040804
classloader.ClassLoaderTest@7f31245a
Copy after login

扩展:命名空间

1. 每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成

2. 在同一命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类

3. 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类

php中文网,大量的免费Java入门教程,欢迎在线学习!  

The above is the detailed content of Detailed explanation of java class loader ClassLoader. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to solve class loader conflicts in Java development How to solve class loader conflicts in Java development Jun 29, 2023 am 08:32 AM

How to solve class loader conflicts in Java development Introduction: In Java development, class loader conflicts are a common problem. When different class loaders are used to load the same class or resource file, conflicts will occur, causing the program to fail to run properly. This article explains what a class loader conflict is and how to resolve it. 1. What is a class loader conflict? The class loading mechanism in Java adopts the parent delegation model. Each class loader has a parent class loader, and the final parent class loader is the startup class loader. when needed

Java Error: ClassLoader Error, How to Fix and Avoid Java Error: ClassLoader Error, How to Fix and Avoid Jun 25, 2023 am 09:02 AM

During development using Java, we often encounter ClassLoader errors, which are caused by the class loader not finding or loading the correct class. This kind of error will cause the program to fail to execute normally and bring inconvenience to development. Therefore, we need to understand the causes of ClassLoader errors and their solutions in order to better optimize our Java programs. 1. Causes of ClassLoader errors: Classes are loaded through ClassLoader in Java, ClassLoad

How SpringBoot encrypts and protects class files through custom classloader How SpringBoot encrypts and protects class files through custom classloader May 11, 2023 pm 09:07 PM

Background Recently, key business codes have been encrypted for the company framework to prevent the engineering code from being easily restored through decompilation tools such as jd-gui. The configuration and use of the related obfuscation scheme are relatively complex and there are many problems for the springboot project, so the class files are encrypted and then passed The custom classloder is decrypted and loaded. This solution is not absolutely safe. It only increases the difficulty of decompilation. It prevents gentlemen but not villains. The overall encryption protection flow chart is shown in the figure below. Maven plug-in encryption uses custom maven plug-in to compile. The class file specified is encrypted, and the encrypted class file is copied to the specified path. Here, it is saved to resource/corecla.

How to use the ClassLoader function for class loading in Java How to use the ClassLoader function for class loading in Java Jun 26, 2023 pm 04:16 PM

The principles and methods of class loading using the ClassLoader function in Java have always been one of the focuses of Java developers. The ClassLoader function is part of the Java class library. Its main function is to load Java class files into the Java Virtual Machine (JVM) so that the program can run normally. The ClassLoader function is the core of Java class loading. In the Java runtime environment, it is responsible for finding and loading the bytecode of Java classes, so understand and master it.

How to use the ClassLoader function for dynamic class loading in Java How to use the ClassLoader function for dynamic class loading in Java Jun 26, 2023 pm 02:10 PM

The ClassLoader function in Java can dynamically load classes at runtime, which is very useful in applications that require flexible deployment and modification of code. Through the function of ClassLoader, the plug-in mechanism can be implemented to improve the scalability and flexibility of the system. This article will introduce how to use the ClassLoader function for dynamic class loading. 1. The role of ClassLoader When starting the Java Virtual Machine (JVM), three ClassLoaders will be created:

Detailed explanation of PHP auto-loading knowledge points: Unlock a powerful tool to improve your skill threshold Detailed explanation of PHP auto-loading knowledge points: Unlock a powerful tool to improve your skill threshold Feb 19, 2024 pm 03:15 PM

Overview of PHP automatic loading PHP automatic loading means that when using a class, PHP will automatically load the definition file of the class. This is usually achieved through a class loader. A class loader is a program responsible for loading class definition files. It can be built-in or customized. Class loader types There are two types of class loaders built into PHP: Zend class loader: This is PHP's default class loader, which loads class definition files located in PHP's built-in libraries. PSR-4 class loader: PSR-4 is an autoloading standard that defines a set of rules for loading class definition files. The PSR-4 class loader loads class definition files according to the PSR-4 standard. Additionally, class loaders can be customized. Custom class loaders can be customized according to their own

The Art of PHP Autoloading: Exploring Loading Techniques The Art of PHP Autoloading: Exploring Loading Techniques Mar 02, 2024 pm 09:19 PM

Autoloading is a technique that automatically loads PHP classes without the need to manually include each file. It simplifies application development and maintenance, improving performance and maintainability. This article will explore the commonly used automatic loading technology in PHP. SPLAutoloaderSPL (Standard PHP Library) contains a built-in autoloading mechanism called the spl_autoload_reGISter() function. This function allows you to register a loader function that will be called when an attempt is made to load a class that does not exist. The following example demonstrates how to use SPLAutoloader: spl_autoload_register(function($class){

The role of class loader in Java virtual machine The role of class loader in Java virtual machine Apr 13, 2024 pm 02:51 PM

The role of the class loader: Loading: Read class files from the specified source. Verification: Confirm that the class file conforms to the specification. Preparation: allocate memory and initialize static variables. Parse: Parse symbol references. Initialization: Call methods, execute static initialization blocks, and allocate class objects.

See all articles