【Java原理系列】Java中ClassLoader原理用法示例中文源码分析

【Java笔记】反射 - Class类与ClassLoader类 目录 Class类的理解 Class类的实例化 Class实例对应的结构 类的加载与ClassLoader 加载 类加载器的作用 链接 初始化 加载器实例化 使用ClassLoader加载配置文件 Class类的理解 程序经过 javac . exe 命令以后,会生成一个或多个字节码文件( . class 结尾) 接着我们使用 javac . exe 命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行 阅读详情

Java中ClassLoader原理用法示例中文源码分析

必看

类加载器是负责加载类的对象。ClassLoader类是一个抽象类。 给定类的二进制名称,类加载器应该尝试定位或生成构成该类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。

每个Class对象都包含指向定义它的ClassLoader的引用。

数组类的Class对象不是由类加载器创建的,而是在Java运行时根据需要自动创建的。通过getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同; 如果元素类型是原始类型,则数组类没有类加载器。

应用程序可以实现ClassLoader的子类以扩展Java虚拟机动态加载类的方式。

类加载器通常由安全管理器用于指示安全域。

ClassLoader类使用委托模型来搜索类和资源。每个ClassLoader实例都有一个关联的父类加载器。在请求查找类或资源时,ClassLoader实例将在尝试自己查找类或资源之前将类或资源的查找委托给其父类加载器。虚拟机内置的类加载器称为“引导类加载器”,它本身没有父类加载器,但可以作为ClassLoader实例的父类。

支持并发加载类的类加载器称为并行可加载类加载器, 它们需要在其类初始化时通过调用ClassLoader.registerAsParallelCapable()方法进行自我注册。 请注意,默认情况下,ClassLoader类已经注册为并行可加载。 然而,如果子类是并行可加载的,则仍然需要注册自己。在非严格层次结构的环境中,类加载器需要具备并行可加载的能力,否则类加载可能会导致死锁,
因为在整个类加载过程中保持了加载器锁(参见loadClass方法)。

通常,Java虚拟机以平台相关的方式从本地文件系统加载类。 例如,在UNIX系统上,虚拟机从CLASSPATH环境变量定义的目录加载类。但是,有些类可能不来自文件;它们可能来自其他来源,如网络,或者可以由应用程序构造。 defineClass方法将字节数组转换为Class实例。 可以使用这个新定义的类的实例来创建对象。

类加载器创建的对象的方法和构造函数可能引用其他类。 为了确定所引用的类,Java虚拟机会调用最初创建该类的类加载器的loadClass方法。

例如,一个应用程序可以创建一个网络类加载器来从服务器下载类文件。 示例代码如下:

ClassLoader loader = new NetworkClassLoader(host, port);
  Object main = loader.loadClass("Main", true).newInstance();

网络类加载器子类必须定义findClass方法和loadClassData方法来从网络中加载类。 下载组成该类的字节后,应使用defineClass方法创建一个类实例。示例实现如下:

class NetworkClassLoader extends ClassLoader {
      String host;
      int port;
public Class findClass(String name) {
      byte[] b = loadClassData(name);
      return defineClass(name, b, 0, b.length);
  }
 
  private byte[] loadClassData(String name) {
      // 从连接中加载类数据
      ...
  }
}  

在ClassLoader的方法中,作为String参数提供的任何类名都必须是由Java语言规范定义的二进制名称。

二进制名称有效类名的示例包括:
“java.lang.String”, “javax.swing.JSpinner D e f a u l t E d i t o r " , " j a v a . s e c u r i t y . K e y S t o r e DefaultEditor", "java.security.KeyStore DefaultEditor","java.security.KeyStoreBuilder$FileBuilder$1”
“java.net.URLClassLoader$3$1”

原理

Java中的ClassLoader类是用于加载Java类的核心类之一。它负责在运行时查找和加载Java类的字节码,并将其转换为可以在Java虚拟机中执行的可执行代码。

ClassLoader的工作原理如下:

  1. 类加载委派模型:ClassLoader使用一种称为类加载委派模型的机制来加载类。当应用程序请求加载一个类时,ClassLoader首先检查它是否已经被加载过,如果是,则直接返回该类的定义。如果没有加载过,则将请求传递给父级ClassLoader。这个过程会一直向上层ClassLoader进行传递,直到达到顶级的引导类加载器(Bootstrap ClassLoader)。如果顶级引导类加载器无法找到类的定义,那么会回到初始的ClassLoader,尝试自己加载类。

  2. 双亲委派模型:ClassLoader通过双亲委派模型来实现类加载的安全性。根据这个模型,ClassLoader在尝试加载类之前,会先委托给其父级ClassLoader去加载。这样可以确保类的加载是从上到下、从外到内的有序加载。这种机制可以防止恶意代码通过替换系统类库中的类来破坏Java运行环境的安全性。

  3. 查找类文件:当一个ClassLoader需要加载一个类时,它首先会根据类的全限定名转换成类文件的路径。ClassLoader会根据指定的类加载路径(Classpath)来查找类文件。一般情况下,ClassLoader会从文件系统或者JAR文件中查找类文件。

  4. 定义类:当ClassLoader找到类文件后,会读取类文件的字节码,并将其转换为Java虚拟机可执行的格式。然后使用定义类的方法(defineClass())将字节码转换为Java类的定义,并返回一个Class对象。

总之,ClassLoader负责在Java运行时动态地查找、加载和定义类。它通过委派模型和双亲委派机制保证了类加载的顺序和安全性。ClassLoader的工作原理是Java语言实现动态加载和扩展性的重要基础。

用法

以下是对ClassLoader类全部用法的详细描述:

方法描述
loadClass(String name)加载指定名称的类,并返回对应的Class对象。这个方法使用双亲委派模型,从上至下依次尝试加载类。如果找不到类,则会抛出ClassNotFoundException异常。
findClass(String name)查找并加载指定名称的类,并返回对应的Class对象。这个方法一般在自定义ClassLoader中重写,以实现自定义的类查找逻辑。如果找不到类,则需要抛出ClassNotFoundException异常。
defineClass(String name, byte[] b, int off, int len)将字节数组转换为Java类的定义,并返回对应的Class对象。这个方法通常在自定义ClassLoader中被调用,用于加载已经获得的类字节码。
getParent()获取当前ClassLoader的父级ClassLoader。ClassLoader在加载类时会首先委托给父级ClassLoader去加载。如果没有父级ClassLoader,则返回null。
getSystemClassLoader()返回系统默认的ClassLoader。这是应用程序的默认ClassLoader,用于加载类路径上的类。
getClassLoader()获取给定类的ClassLoader。这个方法可以用来获取任意类的ClassLoader,例如通过Class对象的getClassLoader()方法来获取该类的ClassLoader。
setDefaultAssertionStatus(boolean enabled)设置类加载器的默认断言状态。断言状态决定由ClassLoader加载的类是否默认启用或禁用断言。
setPackageAssertionStatus(String packageName, boolean enabled)设置指定包的断言状态。可以通过这个方法来控制指定包及其子包下的类是否启用或禁用断言。
setClassAssertionStatus(String className, boolean enabled)设置指定类的断言状态。可以通过这个方法来控制指定类是否启用或禁用断言。
clearAssertionStatus()清除类加载器的断言状态,将其重置为默认值。这会清除所有已设置的包和类的断言状态设置。

ClassLoader类提供了许多用于加载、查找和定义类的方法,以及管理断言状态的功能。这些方法允许开发人员根据自己的需求进行类加载和管理操作,并实现自定义的类加载逻辑。

示例

下面是一些ClassLoader类的代码示例,展示了其主要用法:

  1. 加载类:
ClassLoader classLoader = MyClass.class.getClassLoader();
Class<?> clazz = classLoader.loadClass("com.example.MyClass");
  1. 查找类:
ClassLoader classLoader = MyClass.class.getClassLoader();
Class<?> clazz = classLoader.findClass("com.example.MyClass");
  1. 自定义ClassLoader并重写findClass()方法:
public class MyClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 自定义类查找逻辑,根据名称加载类字节码
        byte[] classBytes = loadClassBytes(name);
        return defineClass(name, classBytes, 0, classBytes.length);
    }
}

// 使用自定义ClassLoader加载类
MyClassLoader classLoader = new MyClassLoader();
Class<?> clazz = classLoader.loadClass("com.example.MyClass");
  1. 获取父级ClassLoader:
ClassLoader parentClassLoader = MyClass.class.getClassLoader().getParent();
  1. 获取系统ClassLoader:
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
  1. 获取类加载器链:
Class<?> clazz = MyClass.class;
ClassLoader classLoader = clazz.getClassLoader();
while (classLoader != null) {
    System.out.println(classLoader);
    classLoader = classLoader.getParent();
}
  1. 设置默认断言状态:
ClassLoader classLoader = MyClass.class.getClassLoader();
classLoader.setDefaultAssertionStatus(true);
  1. 设置包和类的断言状态:
ClassLoader classLoader = MyClass.class.getClassLoader();
classLoader.setPackageAssertionStatus("com.example", true);
classLoader.setClassAssertionStatus("com.example.MyClass", false);
  1. 清除断言状态:
ClassLoader classLoader = MyClass.class.getClassLoader();
classLoader.clearAssertionStatus();

这些示例展示了ClassLoader类的一些常见用法,涵盖了加载、查找、定义类以及断言状态管理等功能。根据具体需求,可以选择适合的方法来实现所需的类加载和管理操作。

中文源码

/**
 * 类加载器是负责加载类的对象。ClassLoader类是一个抽象类。
 * 给定类的二进制名称,类加载器应该尝试定位或生成构成该类定义的数据。
 * 典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
 *
 * 每个Class对象都包含指向定义它的ClassLoader的引用。
 *
 * 数组类的Class对象不是由类加载器创建的,而是在Java运行时根据需要自动创建的。
 * 通过getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同;
 * 如果元素类型是原始类型,则数组类没有类加载器。
 *
 * 应用程序可以实现ClassLoader的子类以扩展Java虚拟机动态加载类的方式。
 *
 * 类加载器通常由安全管理器用于指示安全域。
 *
 * ClassLoader类使用委托模型来搜索类和资源。
 * 每个ClassLoader实例都有一个关联的父类加载器。
 * 在请求查找类或资源时,ClassLoader实例将在尝试自己查找类或资源之前将类或资源的查找委托给其父类加载器。
 * 虚拟机内置的类加载器称为“引导类加载器”,它本身没有父类加载器,但可以作为ClassLoader实例的父类。
 *
 * 支持并发加载类的类加载器称为并行可加载类加载器,
 * 它们需要在其类初始化时通过调用ClassLoader.registerAsParallelCapable()方法进行自我注册。
 * 请注意,默认情况下,ClassLoader类已经注册为并行可加载。
 * 然而,如果子类是并行可加载的,则仍然需要注册自己。
 * 在非严格层次结构的环境中,类加载器需要具备并行可加载的能力,否则类加载可能会导致死锁,
 * 因为在整个类加载过程中保持了加载器锁(参见loadClass方法)。
 *
 * 通常,Java虚拟机以平台相关的方式从本地文件系统加载类。
 * 例如,在UNIX系统上,虚拟机从CLASSPATH环境变量定义的目录加载类。
 *
 * 但是,有些类可能不来自文件;它们可能来自其他来源,如网络,或者可以由应用程序构造。
 * defineClass方法将字节数组转换为Class实例。
 * 可以使用这个新定义的类的实例来创建对象。
 *
 * 类加载器创建的对象的方法和构造函数可能引用其他类。
 * 为了确定所引用的类,Java虚拟机会调用最初创建该类的类加载器的loadClass方法。
 *
 * 例如,一个应用程序可以创建一个网络类加载器来从服务器下载类文件。
 * 示例代码如下:
 *
 * ClassLoader loader = new NetworkClassLoader(host, port);
 * Object main = loader.loadClass("Main", true).newInstance();
 * ...
 *
 * 网络类加载器子类必须定义findClass方法和loadClassData方法来从网络中加载类。
 * 下载组成该类的字节后,应使用defineClass方法创建一个类实例。示例实现如下:
 *
 * class NetworkClassLoader extends ClassLoader {
 *     String host;
 *     int port;
 *
 *     public Class findClass(String name) {
 *         byte[] b = loadClassData(name);
 *         return defineClass(name, b, 0, b.length);
 *     }
 *
 *     private byte[] loadClassData(String name) {
 *         // 从连接中加载类数据
 *         ...
 *     }
 * }
 *
 * 二进制名称
 *
 * 在ClassLoader的方法中,作为String参数提供的任何类名都必须是由Java语言规范定义的二进制名称。
 *
 * 有效类名的示例包括:
 * "java.lang.String"
 * "javax.swing.JSpinner$DefaultEditor"
 * "java.security.KeyStore$Builder$FileBuilder$1"
 * "java.net.URLClassLoader$3$1"
 *
 * @see      #resolveClass(Class)
 * @since 1.0
 */
public abstract class ClassLoader {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    // 用于委派的父类加载器
    // 注意:虚拟机硬编码了这个字段的偏移量,因此所有新字段必须添加到它之后。
    private final ClassLoader parent;

    /**
     * 封装并行可加载器类型集合。
     */
    private static class ParallelLoaders {
        private ParallelLoaders() {}

        // 并行可加载器类型集合
        private static final Set<Class<? extends ClassLoader>> loaderTypes =
            Collections.newSetFromMap(
                new WeakHashMap<Class<? extends ClassLoader>, Boolean>());
        static {
            synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); }
        }

        /**
         * 注册给定的类加载器类型为并行可加载。
         * 如果成功注册,则返回true;如果加载器的父类未注册,则返回false。
         */
        static boolean register(Class<? extends ClassLoader> c) {
            synchronized (loaderTypes) {
                if (loaderTypes.contains(c.getSuperclass())) {
                    // 仅当所有超类都是并行可加载时,才将类加载器注册为并行可加载。
                    // 注意:在当前的类加载顺序下,如果直接超类是并行可加载的,
                    // 所有更高级别的超类也必须是并行可加载的。
                    loaderTypes.add(c);
                    return true;
                } else {
                    return false;
                }
            }
        }

        /**
         * 如果给定的类加载器类型已注册为并行可加载,则返回true。
         */
        static boolean isRegistered(Class<? extends ClassLoader> c) {
            synchronized (loaderTypes) {
                return loaderTypes.contains(c);
            }
        }
    }

    // 当前类加载器支持并发加载类时,将类名映射到相应的锁对象。
    // 注意:虚拟机还使用此字段来确定当前类加载器是否支持并发加载,并找到类加载的适当锁对象。
    private final ConcurrentHashMap<String, Object> parallelLockMap;

    // 将包名映射到相应证书的哈希表
    private final Map <String, Certificate[]> package2certs;

    // 所有无证书类共享的数组
    private static final Certificate[] nocerts = new Certificate[0];

    // 由该类加载器加载的类。此表的唯一目的是保持类不被GC,直到类加载器被GC。
    private final Vector<Class<?>> classes = new Vector<>();

    // 此类加载器定义的包。每个包名称都映射到其对应的Package对象。
    private final HashMap<String, Package> packages = new HashMap<>();

    private static Void checkCreateClassLoader() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        return null;
    }

    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            assertionLock = new Object();
        } else {
            // 没有更细粒度的锁;锁定类加载器实例本身
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            assertionLock = this;
        }
    }

    /**
     * 使用指定的父类加载器创建一个新的类加载器。
     *
     * 如果存在安全管理器,则调用其checkCreateClassLoader方法。
     * 这可能会导致安全异常。
     *
     * @param  parent
     *         父类加载器
     *
     * @throws  SecurityException
     *          如果存在安全管理器并且其checkCreateClassLoader方法不允许创建新的类加载器。
     *
     * @since  1.2
     */
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

    /**
     * 使用由getSystemClassLoader()方法返回的ClassLoader作为父类加载器创建一个新的类加载器。
     *
     * 如果存在安全管理器,则调用其checkCreateClassLoader方法。
     * 这可能会导致安全异常。
     *
     * @throws  SecurityException
     *          如果存在安全管理器并且其checkCreateClassLoader方法不允许创建新的类加载器。
     */
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

    // -- Class --

    /**
     * 加载具有指定二进制名称的类。
     * 此方法与loadClass(String, boolean)方法相同地搜索类。
     * 它是Java虚拟机解析类引用时调用的。
     * 调用此方法等效于调用loadClass(name, false)。
     *
     * @param  name
     *         类的二进制名称
     *
     * @return  结果的Class对象
     *
     * @throws  ClassNotFoundException
     *          如果找不到类
     */
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
 
/**
 * 使用指定的二进制名称加载类。
 * 此方法的默认实现按以下顺序搜索类:
 *
 * 1. 调用findLoadedClass(String)检查类是否已经被加载。
 * 2. 在父类加载器上调用loadClass(String)方法。
 *    如果父类加载器为null,则使用虚拟机内置的类加载器。
 * 3. 调用findClass(String)方法查找类。
 *
 * 如果使用上述步骤找到了类,并且resolve标志为true,
 * 则此方法将在生成的Class对象上调用resolveClass(Class)方法。
 *
 * 鼓励ClassLoader的子类重写findClass(String)方法,而不是这个方法。
 *
 * 除非重写,否则此方法在整个类加载过程中都会同步于getClassLoadingLock(String)方法的结果。
 *
 * @param  name
 *         类的二进制名称
 *
 * @param  resolve
 *         是否解析类
 *
 * @return  结果的Class对象
 *
 * @throws  ClassNotFoundException
 *          如果找不到类
 */
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先,检查类是否已经加载
        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异常
            }

            if (c == null) {
                // 如果仍然找不到,则调用findClass方法查找类
                long t1 = System.nanoTime();
                c = findClass(name);

                // 这是定义类加载器;记录统计信息
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

/**
 * 返回类加载操作的锁对象。
 * 为了向后兼容性,此方法的默认实现如下所示:
 * 如果此ClassLoader对象已注册为并行可加载,则该方法返回与指定类名相关联的专用对象。
 * 否则,该方法返回此ClassLoader对象。
 *
 * @param  className
 *         要加载的类的名称
 *
 * @return 类加载操作的锁
 *
 * @throws NullPointerException
 *         如果已注册为并行可加载并且className为null
 *
 * @see #loadClass(String, boolean)
 *
 * @since  1.7
 */
protected Object getClassLoadingLock(String className) {
    Object lock = this;
    if (parallelLockMap != null) {
        Object newLock = new Object();
        lock = parallelLockMap.putIfAbsent(className, newLock);
        if (lock == null) {
            lock = newLock;
        }
    }
    return lock;
}

// JVM在使用此加载器加载类后调用此方法。
private void checkPackageAccess(Class<?> cls, ProtectionDomain pd) {
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        if (ReflectUtil.isNonPublicProxyClass(cls)) {
            for (Class<?> intf: cls.getInterfaces()) {
                checkPackageAccess(intf, pd);
            }
            return;
        }

        final String name = cls.getName();
        final int i = name.lastIndexOf('.');
        if (i != -1) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    sm.checkPackageAccess(name.substring(0, i));
                    return null;
                }
            }, new AccessControlContext(new ProtectionDomain[] {pd}));
        }
    }
}

/**
 * 查找具有指定二进制名称的类。
 * 此方法应该由按照委派模型加载类的类加载器实现重写,
 * 并且将在loadClass方法检查父类加载器是否找到所请求的类后调用。
 * 默认实现会抛出ClassNotFoundException异常。
 *
 * @param  name
 *         类的二进制名称
 *
 * @return  结果的Class对象
 *
 * @throws  ClassNotFoundException
 *          如果找不到类
 *
 * @since  1.2
 */
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

/**
 * 将字节数组转换为Class实例。
 * 在使用Class之前,必须解析Class。
 *
 * <p>此方法为新定义的类分配了默认的ProtectionDomain。
 * 该ProtectionDomain实际上被授予与调用Policy.getPolicy().getPermissions(new CodeSource(null, null))时返回的权限集相同的权限。
 * 默认域在第一次调用defineClass方法时创建,并在后续调用中重复使用。
 *
 * <p>要为类分配特定的ProtectionDomain,请使用接受ProtectionDomain作为其参数之一的defineClass方法。
 *
 * @param  name
 *         类的预期二进制名称,如果未知则为null
 *
 * @param  b
 *         组成类数据的字节。位于b[off]到b[off+len-1]的位置的字节应该具有由Java虚拟机规范定义的有效类文件格式。
 *
 * @param  off
 *         类数据在b中的起始偏移量
 *
 * @param  len
 *         类数据的长度
 *
 * @return  从指定的类数据创建的Class对象
 *
 * @throws  ClassFormatError
 *          如果数据不包含有效的类
 *
 * @throws  IndexOutOfBoundsException
 *          如果off或len为负数,或者off+len大于b.length
 *
 * @throws  SecurityException
 *          如果尝试将此类添加到包中,该包包含了与此类不同的一组证书签名的类,
 *          或者如果尝试在完全限定名称以“java.”开头的包中定义类。
 *
 * @see  #loadClass(String, boolean)
 * @see  #resolveClass(Class)
 *
 * @deprecated  使用defineClass(String, byte[], int, int)方法替代
 */
@Deprecated
protected final Class<?> defineClass(byte[] b, int off, int len)
    throws ClassFormatError
{
    return defineClass(null, b, off, len, null);
}

/**
 * 将字节数组转换为Class实例。
 * 在使用Class之前,必须解析Class。
 *
 * <p>此方法为新定义的类分配了默认的ProtectionDomain。
 * 该ProtectionDomain实际上被授予与调用Policy.getPolicy().getPermissions(new CodeSource(null, null))时返回的权限集相同的权限。
 * 默认域在第一次调用defineClass方法时创建,并在后续调用中重复使用。
 *
 * @param  name
 *         类的预期二进制名称,如果未知则为null
 *
 * @param  b
 *         组成类数据的字节。位于b[off]到b[off+len-1]的位置的字节应该具有由Java虚拟机规范定义的有效类文件格式。
 *
 * @param  off
 *         类数据在b中的起始偏移量
 *
 * @param  len
 *         类数据的长度
 *
 * @return  从指定的类数据创建的Class对象
 *
 * @throws  ClassFormatError
 *          如果数据不包含有效的类
 *
 * @throws  IndexOutOfBoundsException
 *          如果off或len为负数,或者off+len大于b.length
 *
 * @throws  SecurityException
 *          如果尝试将此类添加到包中,该包包含了与此类不同的一组证书签名的类,
 *          或者如果尝试在完全限定名称以“java.”开头的包中定义类。
 *
 * @see  #loadClass(String, boolean)
 * @see  #resolveClass(Class)
 * @see  java.security.CodeSource
 * @see  java.security.SecureClassLoader
 *
 * @since  1.1
 */
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
    throws ClassFormatError
{
    return defineClass(name, b, off, len, null);
}

// 确定保护域,并检查:
// - 不定义java.*类,
// - 此类的签名者与包中其他类的签名者匹配。
private ProtectionDomain preDefineClass(String name,
                                        ProtectionDomain pd)
{
    if (!checkName(name))
        throw new NoClassDefFoundError("IllegalName: " + name);

    // 注意:java.lang.invoke.MemberName.checkForTypeAlias中的检查逻辑依赖于以下事实:如果一个类具有"java.*"形式的名称,则无法伪造该类。
    if ((name != null) && name.startsWith("java.")) {
        throw new SecurityException
            ("Prohibited package name: " +
             name.substring(0, name.lastIndexOf('.')));
    }
    if (pd == null) {
        pd = defaultDomain;
    }

    if (name != null) checkCerts(name, pd.getCodeSource());

    return pd;
}
 
private String defineClassSourceLocation(ProtectionDomain pd)
{
    CodeSource cs = pd.getCodeSource();
    String source = null;
    if (cs != null && cs.getLocation() != null) {
        source = cs.getLocation().toString();
    }
    return source;
}

private void postDefineClass(Class<?> c, ProtectionDomain pd)
{
    if (pd.getCodeSource() != null) {
        Certificate certs[] = pd.getCodeSource().getCertificates();
        if (certs != null)
            setSigners(c, certs);
    }
}

/**
 * 将字节数组转换为<tt>Class</tt>类的实例,可以附加<tt>ProtectionDomain</tt>。
 * 如果域为<tt>null</tt>,则将为该类分配一个默认的域,如{@link #defineClass(String, byte[], int, int)}中所述。
 * 在使用该类之前,必须对其进行解析。
 *
 * <p>在一个包中定义的第一个类决定了该包中所有后续定义的类必须包含的确切证书集合。
 * 类的证书集合是从类的<tt>ProtectionDomain</tt>中的{@link java.security.CodeSource <tt>CodeSource</tt>}获得的。
 * 添加到该包中的任何类都必须包含相同的证书集合,否则将抛出<tt>SecurityException</tt>异常。
 * 注意,如果<tt>name</tt>为<tt>null</tt>,则不执行此检查。
 * 您应始终传递要定义的类的<a href="#name">二进制名称</a>以及字节。
 * 这样可以确保要定义的类确实是您认为的那个类。
 *
 * <p>指定的<tt>name</tt>不能以“<tt>java.</tt>”开头,因为“<tt>java.*</tt>”包中的所有类只能由引导类加载器定义。
 * 如果<tt>name</tt>不是<tt>null</tt>,则它必须等于通过字节数组“<tt>b</tt>”指定的类的<a href="#name">二进制名称</a>,否则将抛出{@link NoClassDefFoundError <tt>NoClassDefFoundError</tt>}异常。
 *
 * @param  name
 *         类的期望<a href="#name">二进制名称</a>,如果未知,则为<tt>null</tt>
 *
 * @param  b
 *         组成类数据的字节数组。从位置<tt>off</tt>到<tt>off+len-1</tt>的字节应具有有效类文件的格式,如<cite>Java&trade;虚拟机规范</cite>中所定义。
 *
 * @param  off
 *         类数据在<tt>b</tt>中的起始偏移量
 *
 * @param  len
 *         类数据的长度
 *
 * @param  protectionDomain
 *         类的ProtectionDomain
 *
 * @return  从数据创建的<tt>Class</tt>对象和可选的<tt>ProtectionDomain</tt>
 *
 * @throws  ClassFormatError
 *          如果数据不包含有效的类
 *
 * @throws  NoClassDefFoundError
 *          如果<tt>name</tt>不等于<tt>b</tt>指定的类的<a href="#name">二进制名称</a>
 *
 * @throws  IndexOutOfBoundsException
 *          如果<tt>off</tt>或<tt>len</tt>为负数,或者<tt>off+len</tt>大于<tt>b.length</tt>
 *
 * @throws  SecurityException
 *          如果尝试将此类添加到包中,该包包含由与此类不同的证书集签名的类,或者如果<tt>name</tt>以“<tt>java.</tt>”开头
 */
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;
}

/**
 * 将{@link java.nio.ByteBuffer <tt>ByteBuffer</tt>}转换为<tt>Class</tt>类的实例,可以附加<tt>ProtectionDomain</tt>。
 * 如果域为<tt>null</tt>,则将为该类分配一个默认的域,如{@link #defineClass(String, byte[], int, int)}中所述。
 * 在使用该类之前,必须对其进行解析。
 *
 * <p>关于在包中定义的第一个类确定包的证书集合的规则以及类名的限制与{@link #defineClass(String, byte[], int, int, ProtectionDomain)}中的文档中指定的相同。
 *
 * <p>以<i>cl</i><tt>.defineClass(</tt><i>name</i><tt>,</tt>
 * <i>bBuffer</i><tt>,</tt> <i>pd</i><tt>)</tt>形式调用此方法将产生与以下语句完全相同的结果
 *
 *<p> <tt>
 * ...<br>
 * byte[] temp = new byte[bBuffer.{@link
 * java.nio.ByteBuffer#remaining remaining}()];<br>
 *     bBuffer.{@link java.nio.ByteBuffer#get(byte[])
 * get}(temp);<br>
 *     return {@link #defineClass(String, byte[], int, int, ProtectionDomain)
 * cl.defineClass}(name, temp, 0,
 * temp.length, pd);<br>
 * </tt></p>
 *
 * @param  name
 *         类的期望<a href="#name">二进制名称</a>,如果未知,则为<tt>null</tt>
 *
 * @param  b
 *         组成类数据的字节数组。从位置<tt>b.position()</tt>到<tt>b.position() + b.limit() -1</tt>的字节应具有有效类文件的格式,如<cite>Java&trade;虚拟机规范</cite>中所定义。
 *
 * @param  protectionDomain
 *         类的ProtectionDomain,或<tt>null</tt>
 *
 * @return  从数据创建的<tt>Class</tt>对象和可选的<tt>ProtectionDomain</tt>
 *
 * @throws  ClassFormatError
 *          如果数据不包含有效的类
 *
 * @throws  NoClassDefFoundError
 *          如果<tt>name</tt>不等于<tt>b</tt>指定的类的<a href="#name">二进制名称</a>
 *
 * @throws  SecurityException
 *          如果尝试将此类添加到包中,该包包含由与此类不同的证书集签名的类,或者如果<tt>name</tt>以“<tt>java.</tt>”开头
 *
 * @see      #defineClass(String, byte[], int, int, ProtectionDomain)
 *
 * @since  1.5
 */
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
                                     ProtectionDomain protectionDomain)
    throws ClassFormatError
{
    int len = b.remaining();

    // 如果不是直接的ByteBufer,则使用byte[]
    if (!b.isDirect()) {
        if (b.hasArray()) {
            return defineClass(name, b.array(),
                               b.position() + b.arrayOffset(), len,
                               protectionDomain);
        } else {
            // 没有数组或只读数组
            byte[] tb = new byte[len];
            b.get(tb);  // 从字节缓冲区中获取字节。
            return defineClass(name, tb, 0, len, protectionDomain);
        }
    }

    protectionDomain = preDefineClass(name, protectionDomain);
    String source = defineClassSourceLocation(protectionDomain);
    Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
    postDefineClass(c, protectionDomain);
    return c;
}

private native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                     ProtectionDomain pd);

private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                     ProtectionDomain pd, String source);

private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
                                     int off, int len, ProtectionDomain pd,
                                     String source);

// 如果名称为null或潜在是有效的二进制名称,则为true
private boolean checkName(String name) {
    if ((name == null) || (name.length() == 0))
        return true;
    if ((name.indexOf('/') != -1)
        || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
        return false;
    return true;
}

private void checkCerts(String name, CodeSource cs) {
    int i = name.lastIndexOf('.');
    String pname = (i == -1) ? "" : name.substring(0, i);

    Certificate[] certs = null;
    if (cs != null) {
        certs = cs.getCertificates();
    }
    Certificate[] pcerts = null;
    if (parallelLockMap == null) {
        synchronized (this) {
            pcerts = package2certs.get(pname);
            if (pcerts == null) {
                package2certs.put(pname, (certs == null? nocerts:certs));
            }
        }
    } else {
        pcerts = ((ConcurrentHashMap<String, Certificate[]>)package2certs).
            putIfAbsent(pname, (certs == null? nocerts:certs));
    }
    if (pcerts != null && !compareCerts(pcerts, certs)) {
        throw new SecurityException("class \""+ name +
             "\"'s signer information does not match signer information of other classes in the same package");
    }
}

/**
 * 检查新类(certs)的证书是否与包中第一个插入的类(pcerts)的证书相同。
 */
private boolean compareCerts(Certificate[] pcerts,
                             Certificate[] certs)
{
    // 证书可以为null,表示没有证书。
    if ((certs == null) || (certs.length == 0)) {
        return pcerts.length == 0;
    }

    // 此时长度必须相同
    if (certs.length != pcerts.length)
        return false;

    // 检查一个数组中的所有证书是否都在另一个数组中,并反之亦然。
    boolean match;
    for (int i = 0; i < certs.length; i++) {
        match = false;
        for (int j = 0; j < pcerts.length; j++) {
            if (certs[i].equals(pcerts[j])) {
                match = true;
                break;
            }
        }
        if (!match) return false;
    }

    // 现在对于pcerts执行相同的操作
    for (int i = 0; i < pcerts.length; i++) {
        match = false;
        for (int j = 0; j < certs.length; j++) {
            if (pcerts[i].equals(certs[j])) {
                match = true;
                break;
            }
        }
        if (!match) return false;
    }

    return true;
}

/**
 * 链接指定的类。这个(误导性命名的)方法可以被类加载器用来链接一个类。
 * 如果类<c>已经被链接,那么这个方法只是简单地返回。否则,该类将按照<c>中的“执行”章节中所述进行链接。
 *
 * @param  c
 *         要链接的类
 *
 * @throws  NullPointerException
 *          如果<c>是<tt>null</tt>
 *
 * @see  #defineClass(String, byte[], int, int)
 */
protected final void resolveClass(Class<?> c) {
    resolveClass0(c);
}

private native void resolveClass0(Class<?> c);

    /**
     * 查找具有指定<a href="#name">二进制名称</a>的类,如果需要加载它。
     *
     * <p>该方法通过系统类加载器(参见{@link #getSystemClassLoader()})加载类。返回的<tt>Class</tt>对象可能与一个以上的<tt>ClassLoader</tt>关联。
     * <tt>ClassLoader</tt>的子类通常不需要调用此方法,因为大多数类加载器只需覆盖{@link #findClass(String)}。</p>
     *
     * @param  name
     *         类的<a href="#name">二进制名称</a>
     *
     * @return  指定<tt>name</tt>的<tt>Class</tt>对象
     *
     * @throws  ClassNotFoundException
     *          如果找不到类
     *
     * @see  #ClassLoader(ClassLoader)
     * @see  #getParent()
     */
    protected final Class<?> findSystemClass(String name)
        throws ClassNotFoundException
    {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            if (!checkName(name))
                throw new ClassNotFoundException(name);
            Class<?> cls = findBootstrapClass(name);
            if (cls == null) {
                throw new ClassNotFoundException(name);
            }
            return cls;
        }
        return system.loadClass(name);
    }

    /**
     * 返回由引导类加载器加载的类;如果未找到,则返回null。
     */
    private Class<?> findBootstrapClassOrNull(String name)
    {
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
    }

    // 如果未找到,则返回null
    private native Class<?> findBootstrapClass(String name);

    /**
     * 如果Java虚拟机将其记录为具有该<a href="#name">二进制名称</a>的类的启动加载器的初始化加载器,则返回具有给定<a href="#name">二进制名称</a>的类。
     * 否则,返回<tt>null</tt>。
     *
     * @param  name
     *         类的<a href="#name">二进制名称</a>
     *
     * @return  <tt>Class</tt>对象,如果未加载类则返回<tt>null</tt>
     *
     * @since  1.1
     */
    protected final Class<?> findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

    private native final Class<?> findLoadedClass0(String name);

    /**
     * 设置类的签名者。在定义类后应调用此方法。
     *
     * @param  c
     *         <tt>Class</tt>对象
     *
     * @param  signers
     *         类的签名者
     *
     * @since  1.1
     */
    protected final void setSigners(Class<?> c, Object[] signers) {
        c.setSigners(signers);
    }


    // -- 资源 --

    /**
     * 查找具有给定名称的资源。资源是一些数据(图像、音频、文本等),可以由类代码以与代码位置无关的方式访问。
     *
     * <p>资源的名称是一个'/'分隔的路径名,用于标识资源。</p>
     *
     * <p>此方法首先搜索父类加载器中的资源;如果父级为<tt>null</tt>,则搜索内置于虚拟机中的类加载器的路径。如果找不到资源,则该方法将调用{@link #findResource(String)}来查找资源。</p>
     *
     * @apiNote 当重写此方法时,建议实现确保任何委托与{@link #getResources(java.lang.String) getResources(String)}方法一致。
     *
     * @param  name
     *         资源名称
     *
     * @return 用于读取资源的<tt>URL</tt>对象,如果找不到资源或调用者没有足够的权限获取资源,则返回<tt>null</tt>
     *
     * @since  1.1
     */
    public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

    /**
     * 查找具有给定名称的所有资源。资源是一些数据(图像、音频、文本等),可以由类代码以与代码位置无关的方式访问。
     *
     * <p>资源的名称是一个'/'分隔的路径名,用于标识资源。</p>
     *
     * <p>搜索顺序在{@link #getResource(String)}的文档中描述。</p>
     *
     * @param  name
     *         资源名称
     *
     * @return 资源的{@link java.net.URL <tt>URL</tt>}对象的枚举。如果找不到资源,则枚举为空。类加载器无法访问的资源将不在枚举中。
     *
     * @throws  IOException
     *          如果发生I/O错误

     * @since  1.2
     */
    public Enumeration<URL> getResources(String name) throws IOException {
        @SuppressWarnings("unchecked")
        Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);

        return new CompoundEnumeration<>(tmp);
    }

    /**
     * 查找具有给定名称的资源。类加载器实现应覆盖此方法以指定资源的查找位置。
     *
     * @param  name
     *         资源名称
     *
     * @return 用于读取资源的<tt>URL</tt>对象,如果找不到资源则返回<tt>null</tt>
     *
     * @since  1.2
     */
    protected URL findResource(String name) {
        return null;
    }

    /**
     * 返回表示具有给定名称的所有资源的{@link java.net.URL <tt>URL</tt>}对象的枚举。类加载器实现应覆盖此方法以指定从何处加载资源。
     *
     * @param  name
     *         资源名称
     *
     * @return 用于资源的{@link java.net.URL <tt>URL</tt>}对象的枚举
     *
     * @throws  IOException
     *          如果发生I/O错误
     *
     * @since  1.2
     */
    protected Enumeration<URL> findResources(String name) throws IOException {
        return java.util.Collections.emptyEnumeration();
    }

    /**
     * 将调用者注册为并行可用。
     * 当且仅当满足以下所有条件时,注册才成功:
     * <ol>
     * <li> 调用者的任何实例尚未创建</li>
     * <li> 调用者的所有超类(除了Object类)都已注册为并行可用</li>
     * </ol>
     * <p>请注意,一旦将类加载器注册为并行可用,就无法更改它。</p>
     *
     * @return  如果成功将调用者注册为并行可用,则返回true;否则返回false。
     *
     * @since   1.7
     */
    @CallerSensitive
    protected static boolean registerAsParallelCapable() {
        Class<? extends ClassLoader> callerClass =
            Reflection.getCallerClass().asSubclass(ClassLoader.class);
        return ParallelLoaders.register(callerClass);
    }

    /**
     * 从用于加载类的搜索路径中查找具有指定名称的资源。此方法通过系统类加载器(参见{@link #getSystemClassLoader()})定位资源。
     *
     * @param  name
     *         资源名称
     *
     * @return 用于读取资源的{@link java.net.URL <tt>URL</tt>}对象,如果找不到资源则返回<tt>null</tt>
     *
     * @since  1.1
     */
    public static URL getSystemResource(String name) {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            return getBootstrapResource(name);
        }
        return system.getResource(name);
    }

    /**
     * 从用于加载类的搜索路径中查找具有指定名称的所有资源。找到的资源将作为{@link java.util.Enumeration <tt>Enumeration</tt>}的{@link java.net.URL <tt>URL</tt>}对象返回。
     *
     * <p>搜索顺序在{@link #getSystemResource(String)}的文档中描述。</p>
     *
     * @param  name
     *         资源名称
     *
     * @return 资源{@link java.net.URL <tt>URL</tt>}对象的枚举
     *
     * @throws  IOException
     *          如果发生I/O错误

     * @since  1.2
     */
    public static Enumeration<URL> getSystemResources(String name)
        throws IOException
    {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            return getBootstrapResources(name);
        }
        return system.getResources(name);
    }

    /**
     * 从VM的内置类加载器中查找资源。
     */
    private static URL getBootstrapResource(String name) {
        URLClassPath ucp = getBootstrapClassPath();
        Resource res = ucp.getResource(name);
        return res != null ? res.getURL() : null;
    }

    /**
     * 从VM的内置类加载器中查找资源。
     */
    private static Enumeration<URL> getBootstrapResources(String name)
        throws IOException
    {
        final Enumeration<Resource> e =
            getBootstrapClassPath().getResources(name);
        return new Enumeration<URL> () {
            public URL nextElement() {
                return e.nextElement().getURL();
            }
            public boolean hasMoreElements() {
                return e.hasMoreElements();
            }
        };
    }

    // 返回用于查找系统资源的URLClassPath。
    static URLClassPath getBootstrapClassPath() {
        return sun.misc.Launcher.getBootstrapClassPath();
    }


    /**
     * 返回用于读取指定资源的输入流。
     *
     * <p>搜索顺序在{@link #getResource(String)}的文档中描述。</p>
     *
     * @param  name
     *         资源名称
     *
     * @return 用于读取资源的输入流,如果找不到资源则返回<tt>null</tt>
     *
     * @since  1.1
     */
    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 打开以读取类加载器用于加载类的搜索路径中具有指定名称的资源。此方法通过系统类加载器(参见{@link #getSystemClassLoader()})定位资源。
     *
     * @param  name
     *         资源名称
     *
     * @return 用于读取资源的输入流,如果找不到资源则返回<tt>null</tt>
     *
     * @since  1.1
     */
    public static InputStream getSystemResourceAsStream(String name) {
        URL url = getSystemResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }


    // -- 层次结构 --

    /**
     * 返回用于委托的父类加载器。某些实现可能使用<tt>null</tt>表示引导类加载器。如果此类加载器的父级是引导类加载器,则在此类加载器中,此方法将返回<tt>null</tt>。
     *
     * <p>如果存在安全管理器,并且调用者的类加载器不是<tt>null</tt>并且不是此类加载器的祖先,则此方法将使用{@link
     * SecurityManager#checkPermission(java.security.Permission)
     * <tt>checkPermission</tt>}方法调用安全管理器的以<tt>RuntimePermission("getClassLoader")</tt>权限验证对父类加载器的访问是否被允许。否则,将抛出<tt>SecurityException</tt>。</p>
     *
     * @return  父级<tt>ClassLoader</tt>
     *
     * @throws  SecurityException
     *          如果存在安全管理器并且其<tt>checkPermission</tt>方法不允许对此类加载器的父类加载器的访问。
     *
     * @since  1.2
     */
    @CallerSensitive
    public final ClassLoader getParent() {
        if (parent == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // 检查对父类加载器的访问权限
            // 如果调用者的类加载器与此类加载器相同,则进行权限检查。
            checkClassLoaderPermission(parent, Reflection.getCallerClass());
        }
        return parent;
    }

/**
 * 返回用于委派的系统类加载器。这是新的ClassLoader实例的默认委派父级,并且通常是启动应用程序时使用的类加载器。
 *
 * <p>此方法在运行时的早期启动序列中首先调用,此时它创建系统类加载器并将其设置为调用线程的上下文类加载器。
 *
 * <p>默认的系统类加载器是此类的特定实现。
 *
 * <p>如果在首次调用此方法时定义了系统属性“java.system.class.loader”,则该属性的值将作为系统类加载器返回。该类使用默认的系统类加载器进行加载,并且必须定义一个以类型为ClassLoader的单个参数的公共构造函数,该参数用作委派父级。然后使用默认的系统类加载器作为参数使用此构造函数创建一个实例。生成的类加载器被定义为系统类加载器。
 *
 * <p>如果存在安全管理器,并且调用者的类加载器不为null且调用者的类加载器与系统类加载器不同且不是系统类加载器的祖先,则此方法将使用SecurityManager的{@link SecurityManager#checkPermission(java.security.Permission)<tt> checkPermission </ tt>}方法以{@link RuntimePermission#RuntimePermission(String)<tt> RuntimePermission( "getClassLoader")</tt>}权限验证对系统类加载器的访问权限。否则,将抛出SecurityException。</p>
 *
 * @return 委派的系统<tt> ClassLoader </ tt>,如果没有则为<tt> null </ tt>
 *
 * @throws SecurityException 如果存在安全管理器,并且其<tt> checkPermission </ tt>方法不允许访问系统类加载器。
 *
 * @throws IllegalStateException 如果在由“java.system.class.loader”属性指定的类加载器的构造过程中递归调用
 *
 * @throws Error 如果定义了系统属性“java.system.class.loader”,但无法加载命名类,提供程序类未定义所需构造函数或在调用该构造函数时引发异常。可以通过{@link Throwable#getCause()}方法检索错误的根本原因。
 *
 * @revised 1.4
 */
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}

private static synchronized void initSystemClassLoader() {
    if (!sclSet) {
        if (scl != null)
            throw new IllegalStateException("recursive invocation");
        sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
        if (l != null) {
            Throwable oops = null;
            scl = l.getClassLoader();
            try {
                scl = AccessController.doPrivileged(
                    new SystemClassLoaderAction(scl));
            } catch (PrivilegedActionException pae) {
                oops = pae.getCause();
                if (oops instanceof InvocationTargetException) {
                    oops = oops.getCause();
                }
            }
            if (oops != null) {
                if (oops instanceof Error) {
                    throw (Error) oops;
                } else {
                    // wrap the exception
                    throw new Error(oops);
                }
            }
        }
        sclSet = true;
    }
}

// 如果在此类加载器的委派链中找到指定的类加载器,则返回true。
boolean isAncestor(ClassLoader cl) {
    ClassLoader acl = this;
    do {
        acl = acl.parent;
        if (cl == acl) {
            return true;
        }
    } while (acl != null);
    return false;
}

// 测试类加载器访问是否需要“getClassLoader”权限检查。如果类加载器'from'可以访问类加载器'to',则类加载器'from'与类加载器'to'相同或者是'to'的祖先。系统域中的类加载器可以访问任何类加载器。
private static boolean needsClassLoaderPermissionCheck(ClassLoader from,
                                                       ClassLoader to)
{
    if (from == to)
        return false;

    if (from == null)
        return false;

    return !to.isAncestor(from);
}

// 返回类的类加载器,如果没有则返回null。
static ClassLoader getClassLoader(Class<?> caller) {
    // 如果VM请求该项,则这个值可能为null
    if (caller == null) {
        return null;
    }
    // 由于这是包私有的,所以绕过安全检查
    return caller.getClassLoader0();
}

/*
 * 检查RuntimePermission(“getClassLoader”)权限
 * 如果调用者的类加载器不为null且调用者的类加载器与给定的cl参数不同且不是相同的或给定的cl参数的祖先。
 */
static void checkClassLoaderPermission(ClassLoader cl, Class<?> caller) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        // 如果VM请求该项,则调用者可能为空
        ClassLoader ccl = getClassLoader(caller);
        if (needsClassLoaderPermissionCheck(ccl, cl)) {
            sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
        }
    }
}

// 系统的类加载器
// @GuardedBy(“ClassLoader.class”)
private static ClassLoader scl;

// 设置为true一旦设置了系统类加载器
// @GuardedBy(“ClassLoader.class”)
private static boolean sclSet;


// -- Package --

/**
 * 在此<tt> ClassLoader </ tt>中按名称定义包。这允许类加载器为其类定义包。必须在定义类之前创建包,并且包名在类加载器中必须是唯一的,一旦创建就无法重新定义或更改。
 *
 * @param name 包名
 *
 * @param specTitle 规范标题
 *
 * @param specVersion 规范版本
 *
 * @param specVendor 规范供应商
 *
 * @param implTitle 实现标题
 *
 * @param implVersion 实现版本
 *
 * @param implVendor 实现供应商
 *
 * @param sealBase 如果不是null,则此包使用与给定代码源{@link java.net.URL <tt> URL </ tt>}对象相关联的封闭基础库。否则,该包未封装。
 *
 * @return 新定义的<tt> Package </ tt>对象
 *
 * @throws IllegalArgumentException 如果包名与此类加载器或其祖先之一中的现有包重复
 *
 * @since 1.2
 */
protected Package definePackage(String name, String specTitle,
                                String specVersion, String specVendor,
                                String implTitle, String implVersion,
                                String implVendor, URL sealBase)
    throws IllegalArgumentException
{
    synchronized (packages) {
        Package pkg = getPackage(name);
        if (pkg != null) {
            throw new IllegalArgumentException(name);
        }
        pkg = new Package(name, specTitle, specVersion, specVendor,
                          implTitle, implVersion, implVendor,
                          sealBase, this);
        packages.put(name, pkg);
        return pkg;
    }
}

/**
 * 返回由此类加载器或任何其祖先定义的<tt> Package </ tt>。
 *
 * @param name 包名
 *
 * @return 对应于给定名称的<tt> Package </ tt>,如果未找到则为<tt> null </ tt>
 *
 * @since 1.2
 */
protected Package getPackage(String name) {
    Package pkg;
    synchronized (packages) {
        pkg = packages.get(name);
    }
    if (pkg == null) {
        if (parent != null) {
            pkg = parent.getPackage(name);
        } else {
            pkg = Package.getSystemPackage(name);
        }
        if (pkg != null) {
            synchronized (packages) {
                Package pkg2 = packages.get(name);
                if (pkg2 == null) {
                    packages.put(name, pkg);
                } else {
                    pkg = pkg2;
                }
            }
        }
    }
    return pkg;
}

/**
 * 返回由此类加载器及其祖先定义的所有<tt> Package </ tt>。
 *
 * @return 由此<tt> ClassLoader </ tt>定义的<tt> Package </ tt>对象数组
 *
 * @since 1.2
 */
protected Package[] getPackages() {
    Map<String, Package> map;
    synchronized (packages) {
        map = new HashMap<>(packages);
    }
    Package[] pkgs;
    if (parent != null) {
        pkgs = parent.getPackages();
    } else {
        pkgs = Package.getSystemPackages();
    }
    if (pkgs != null) {
        for (int i = 0; i < pkgs.length; i++) {
            String pkgName = pkgs[i].getName();
            if (map.get(pkgName) == null) {
                map.put(pkgName, pkgs[i]);
            }
        }
    }
    return map.values().toArray(new Package[map.size()]);
}


// -- Native library access --

/**
 * 返回本地库的绝对路径名。VM调用此方法来定位属于使用此类加载器加载的类的本地库。如果此方法返回<tt> null </ tt>,则VM将沿着指定为“<tt> java.library.path </ tt>”属性的路径搜索库。
 *
 * @param libname 库名称
 *
 * @return 本地库的绝对路径
 *
 * @see System#loadLibrary(String)
 * @see System#mapLibraryName(String)
 *
 * @since 1.2
 */
protected String findLibrary(String libname) {
    return null;
}

/**
 * NativeLibrary内部类表示已加载的本地库实例。
 * 每个类加载器都包含一个加载的本地库向量,存储在private字段<tt>nativeLibraries</tt>中。系统加载的本地库被输入到<tt>systemNativeLibraries</tt>向量中。
 *
 * <p>每个本地库都需要特定版本的JNI。这由私有字段<tt>jniVersion</tt>表示。VM在加载库时设置此字段,并且VM使用它将正确的JNI版本传递给本地方法。</p>
 *
 * @see      ClassLoader
 * @since    1.2
 */
static class NativeLibrary {
    // 本地代码中使用的不透明句柄。
    long handle;
    // 本地库所需的JNI环境版本。
    private int jniVersion;
    // 加载库的类,也指示该本地库所属的加载器。
    private final Class<?> fromClass;
    // 本地库的规范名称或静态库名称。
    String name;
    // 指示本地库是否链接到VM中。
    boolean isBuiltin;
    // 指示本地库是否已加载。
    boolean loaded;
    native void load(String name, boolean isBuiltin);

    native long find(String name);
    native void unload(String name, boolean isBuiltin);

    public NativeLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
        this.name = name;
        this.fromClass = fromClass;
        this.isBuiltin = isBuiltin;
    }

    protected void finalize() {
        synchronized (loadedLibraryNames) {
            if (fromClass.getClassLoader() != null && loaded) {
                /* remove the native library name */
                int size = loadedLibraryNames.size();
                for (int i = 0; i < size; i++) {
                    if (name.equals(loadedLibraryNames.elementAt(i))) {
                        loadedLibraryNames.removeElementAt(i);
                        break;
                    }
                }
                /* unload the library. */
                ClassLoader.nativeLibraryContext.push(this);
                try {
                    unload(name, isBuiltin);
                } finally {
                    ClassLoader.nativeLibraryContext.pop();
                }
            }
        }
    }
    // 在VM中调用以确定JNI_Load / JNI_Unload中的上下文类。
    static Class<?> getFromClass() {
        return ClassLoader.nativeLibraryContext.peek().fromClass;
    }
}

// 我们加载的所有本地库名称。
private static Vector<String> loadedLibraryNames = new Vector<>();

// 属于系统类的本地库。
private static Vector<NativeLibrary> systemNativeLibraries = new Vector<>();

// 与类加载器关联的本地库。
private Vector<NativeLibrary> nativeLibraries = new Vector<>();

// 正在加载/卸载的本地库。
private static Stack<NativeLibrary> nativeLibraryContext = new Stack<>();

// 用于搜索库的路径
private static String usr_paths[];
private static String sys_paths[];

private static String[] initializePath(String propname) {
    String ldpath = System.getProperty(propname, "");
    String ps = File.pathSeparator;
    int ldlen = ldpath.length();
    int i, j, n;
    // 计算路径中的分隔符数
    i = ldpath.indexOf(ps);
    n = 0;
    while (i >= 0) {
        n++;
        i = ldpath.indexOf(ps, i + 1);
    }

    // 分配路径数组 - n :'s = n + 1路径元素
    String[] paths = new String[n + 1];

    // 从ldpath中填充数组
    n = i = 0;
    j = ldpath.indexOf(ps);
    while (j >= 0) {
        if (j - i > 0) {
            paths[n++] = ldpath.substring(i, j);
        } else if (j - i == 0) {
            paths[n++] = ".";
        }
        i = j + 1;
        j = ldpath.indexOf(ps, i);
    }
    paths[n] = ldpath.substring(i, ldlen);
    return paths;
}

// 在java.lang.Runtime类中调用以实现load和loadLibrary。
static void loadLibrary(Class<?> fromClass, String name,
                        boolean isAbsolute) {
    ClassLoader loader =
        (fromClass == null) ? null : fromClass.getClassLoader();
    if (sys_paths == null) {
        usr_paths = initializePath("java.library.path");
        sys_paths = initializePath("sun.boot.library.path");
    }
    if (isAbsolute) {
        if (loadLibrary0(fromClass, new File(name))) {
            return;
        }
        throw new UnsatisfiedLinkError("Can't load library: " + name);
    }
    if (loader != null) {
        String libfilename = loader.findLibrary(name);
        if (libfilename != null) {
            File libfile = new File(libfilename);
            if (!libfile.isAbsolute()) {
                throw new UnsatisfiedLinkError(
"ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
            }
            if (loadLibrary0(fromClass, libfile)) {
                return;
            }
            throw new UnsatisfiedLinkError("Can't load " + libfilename);
        }
    }
    for (int i = 0 ; i < sys_paths.length ; i++) {
        File libfile = new File(sys_paths[i], System.mapLibraryName(name));
        if (loadLibrary0(fromClass, libfile)) {
            return;
        }
        libfile = ClassLoaderHelper.mapAlternativeName(libfile);
        if (libfile != null && loadLibrary0(fromClass, libfile)) {
            return;
        }
    }
    if (loader != null) {
        for (int i = 0 ; i < usr_paths.length ; i++) {
            File libfile = new File(usr_paths[i],
                                    System.mapLibraryName(name));
            if (loadLibrary0(fromClass, libfile)) {
                return;
            }
            libfile = ClassLoaderHelper.mapAlternativeName(libfile);
            if (libfile != null && loadLibrary0(fromClass, libfile)) {
                return;
            }
        }
    }
    // Oops, it failed
    throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
}

private static native String findBuiltinLib(String name);

private static boolean loadLibrary0(Class<?> fromClass, final File file) {
    // 检查是否正在尝试访问静态库
    String name = findBuiltinLib(file.getName());
    boolean isBuiltin = (name != null);
    if (!isBuiltin) {
        boolean exists = AccessController.doPrivileged(
            new PrivilegedAction<Object>() {
                public Object run() {
                    return file.exists() ? Boolean.TRUE : null;
                }})
            != null;
        if (!exists) {
            return false;
        }
        try {
            name = file.getCanonicalPath();
        } catch (IOException e) {
            return false;
        }
    }
    ClassLoader loader =
        (fromClass == null) ? null : fromClass.getClassLoader();
    Vector<NativeLibrary> libs =
        loader != null ? loader.nativeLibraries : systemNativeLibraries;
    synchronized (libs) {
        int size = libs.size();
        for (int i = 0; i < size; i++) {
            NativeLibrary lib = libs.elementAt(i);
            if (name.equals(lib.name)) {
                return true;
            }
        }

        synchronized (loadedLibraryNames) {
            if (loadedLibraryNames.contains(name)) {
                throw new UnsatisfiedLinkError
                    ("Native Library " +
                     name +
                     " already loaded in another classloader");
            }
            /* If the library is being loaded (must be by the same thread,
             * because Runtime.load and Runtime.loadLibrary are
             * synchronous). The reason is can occur is that the JNI_OnLoad
             * function can cause another loadLibrary invocation.
             *
             * Thus we can use a static stack to hold the list of libraries
             * we are loading.
             *
             * If there is a pending load operation for the library, we
             * immediately return success; otherwise, we raise
             * UnsatisfiedLinkError.
             */
            int n = nativeLibraryContext.size();
            for (int i = 0; i < n; i++) {
                NativeLibrary lib = nativeLibraryContext.elementAt(i);
                if (name.equals(lib.name)) {
                    if (loader == lib.fromClass.getClassLoader()) {
                        return true;
                    } else {
                        throw new UnsatisfiedLinkError
                            ("Native Library " +
                             name +
                             " is being loaded in another classloader");
                    }
                }
            }
            NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
            nativeLibraryContext.push(lib);
            try {
                lib.load(name, isBuiltin);
            } finally {
                nativeLibraryContext.pop();
            }
            if (lib.loaded) {
                loadedLibraryNames.addElement(name);
                libs.addElement(lib);
                return true;
            }
            return false;
        }
    }
}

// 在VM类链接代码中调用。
static long findNative(ClassLoader loader, String name) {
    Vector<NativeLibrary> libs =
        loader != null ? loader.nativeLibraries : systemNativeLibraries;
    synchronized (libs) {
        int size = libs.size();
        for (int i = 0; i < size; i++) {
            NativeLibrary lib = libs.elementAt(i);
            long entry = lib.find(name);
            if (entry != 0)
                return entry;
        }
    }
    return 0;
}


// -- 断言管理 --

final Object assertionLock;

// 断言检查的默认切换。
// @GuardedBy("assertionLock")
private boolean defaultAssertionStatus = false;

// 将String packageName映射到Boolean package默认断言状态。
// 注意,默认包被放置在null映射键下。如果此字段为null,则我们将断言状态查询委托给VM,即没有此ClassLoader的断言状态修改方法之一已被调用。
// @GuardedBy("assertionLock")
private Map<String, Boolean> packageAssertionStatus = null;

// 将String fullyQualifiedClassName映射到Boolean assertionStatus。如果此字段为null,则我们将断言状态查询委托给VM,即没有此ClassLoader的断言状态修改方法之一已被调用。
// @GuardedBy("assertionLock")
Map<String, Boolean> classAssertionStatus = null;

/**
 * 设置此类加载器的默认断言状态。此设置确定由此类加载器加载并在将来初始化的类是否默认启用或禁用断言。
 * 可以通过调用{@link #setPackageAssertionStatus(String, boolean)}或{@link #setClassAssertionStatus(String, boolean)}来覆盖此设置。
 *
 * @param  enabled
 *         如果由此类加载器加载的类将默认启用断言,则为<tt>true</tt>,如果它们将默认禁用断言,则为<tt>false</tt>。
 *
 * @since  1.4
 */
public void setDefaultAssertionStatus(boolean enabled) {
    synchronized (assertionLock) {
        if (classAssertionStatus == null)
            initializeJavaAssertionMaps();

        defaultAssertionStatus = enabled;
    }
}


/**
 * 设置指定包的包默认断言状态。包默认断言状态确定将来初始化的属于指定包或其“子包”中的类的断言状态。
 *
 * <p> 名为p的包的子包是任何名称以"<tt>p.</tt>"开头的包。例如,<tt>javax.swing.text</tt>是<tt>javax.swing</tt>的子包,<tt>java.util</tt>和<tt>java.lang.reflect</tt>都是<tt>java</tt>的子包。
 *
 * <p> 如果多个包默认适用于给定类,则与最特定包相关联的包默认优先于其他包。例如,如果<tt>javax.lang</tt>和<tt>javax.lang.reflect</tt>都有与之关联的包默认值,则后一个包默认值适用于<tt>javax.lang.reflect</tt>中的类。
 *
 * <p> 包默认优先于类加载器的默认断言状态,并且可以通过调用{@link #setClassAssertionStatus(String, boolean)}在每个类的基础上进行覆盖。</p>
 *
 * @param  packageName
 *         要设置其包默认断言状态的包的名称。null值表示未命名的包,即“当前”包(参见<cite>The Java&trade; Language Specification</cite>第7.4.2节)。
 *
 * @param  enabled
 *         如果由此类加载器加载并且属于指定包或其子包中的类将默认启用断言,则为<tt>true</tt>;如果它们将默认禁用断言,则为<tt>false</tt>。
 *
 * @since  1.4
 */
public void setPackageAssertionStatus(String packageName,
                                      boolean enabled) {
    synchronized (assertionLock) {
        if (packageAssertionStatus == null)
            initializeJavaAssertionMaps();

        packageAssertionStatus.put(packageName, enabled);
    }
}

/**
 * 设置此类加载器中指定顶级类及其包含的任何嵌套类的所需断言状态。该设置优先于类加载器的默认断言状态和适用的每个包默认值。如果已经初始化了命名类,则此方法无效。(一旦类被初始化,其断言状态就不能更改。)
 *
 * <p> 如果命名类不是顶级类,则此调用对任何类的实际断言状态没有影响。</p>
 *
 * @param  className
 *         要设置其断言状态的顶级类的完全限定类名。
 *
 * @param  enabled
 *         如果在初始化时启用(如果有的话),则命名类将启用断言;如果禁用断言,则类将禁用断言。
 *
 * @since  1.4
 */
public void setClassAssertionStatus(String className, boolean enabled) {
    synchronized (assertionLock) {
        if (classAssertionStatus == null)
            initializeJavaAssertionMaps();

        classAssertionStatus.put(className, enabled);
    }
}

/**
 * 将此类加载器的默认断言状态设置为<tt>false</tt>,并丢弃与类加载器关联的任何包默认或类断言状态设置。
 * 提供此方法是为了使类加载器可以忽略任何命令行或持久的断言状态设置,并“从一个干净的板开始。”
 *
 * @since  1.4
 */
public void clearAssertionStatus() {
    /*
     * 不管是否初始化了“Java断言映射”,都将其设置为空映射,从而有效地忽略任何现有设置。
     */
    synchronized (assertionLock) {
        classAssertionStatus = new HashMap<>();
        packageAssertionStatus = new HashMap<>();
        defaultAssertionStatus = false;
    }
}

/**
 * 如果在调用此方法时指定的类被初始化,返回该类将被分配的断言状态。
 * 如果已设置命名类的断言状态,则返回最新的设置;否则,如果任何包默认的断言状态与该类相关,则返回最特定相关包默认断言状态的最新设置;否则,返回此类加载器的默认断言状态。
 *
 * @param  className
 *         正在查询其所需断言状态的类的完全限定类名。
 *
 * @return  指定类的所需断言状态。
 *
 * @since  1.4
 */
boolean desiredAssertionStatus(String className) {
    synchronized (assertionLock) {
        // assert classAssertionStatus   != null;
        // assert packageAssertionStatus != null;

        // 检查类条目
        Boolean result = classAssertionStatus.get(className);
        if (result != null)
            return result.booleanValue();

        // 检查最具体包条目
        int dotIndex = className.lastIndexOf(".");
        if (dotIndex < 0) { // 默认包
            result = packageAssertionStatus.get(null);
            if (result != null)
                return result.booleanValue();
        }
        while(dotIndex > 0) {
            className = className.substring(0, dotIndex);
            result = packageAssertionStatus.get(className);
            if (result != null)
                return result.booleanValue();
            dotIndex = className.lastIndexOf(".", dotIndex-1);
        }

        // 返回类加载器默认值
        return defaultAssertionStatus;
    }
}

// 使用VM提供的信息设置断言。
// 注意:只能在同步块内调用
private void initializeJavaAssertionMaps() {
    // assert Thread.holdsLock(assertionLock);

    classAssertionStatus = new HashMap<>();
    packageAssertionStatus = new HashMap<>();
    AssertionStatusDirectives directives = retrieveDirectives();

    for(int i = 0; i < directives.classes.length; i++)
        classAssertionStatus.put(directives.classes[i],
                                 directives.classEnabled[i]);

    for(int i = 0; i < directives.packages.length; i++)
        packageAssertionStatus.put(directives.packages[i],
                                   directives.packageEnabled[i]);

    defaultAssertionStatus = directives.deflt;
}

// 从VM检索断言指令。
private static native AssertionStatusDirectives retrieveDirectives();
}


class SystemClassLoaderAction
    implements PrivilegedExceptionAction<ClassLoader> {
    private ClassLoader parent;

    SystemClassLoaderAction(ClassLoader parent) {
        this.parent = parent;
    }

    public ClassLoader run() throws Exception {
        String cls = System.getProperty("java.system.class.loader");
        if (cls == null) {
            return parent;
        }

        Constructor<?> ctor = Class.forName(cls, true, parent)
            .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
        ClassLoader sys = (ClassLoader) ctor.newInstance(
            new Object[] { parent });
        Thread.currentThread().setContextClassLoader(sys);
        return sys;
    }
}
booleanValue();
        }
        while(dotIndex > 0) {
            className = className.substring(0, dotIndex);
            result = packageAssertionStatus.get(className);
            if (result != null)
                return result.booleanValue();
            dotIndex = className.lastIndexOf(".", dotIndex-1);
        }

        // 返回类加载器默认值
        return defaultAssertionStatus;
    }
}

// 使用VM提供的信息设置断言。
// 注意:只能在同步块内调用
private void initializeJavaAssertionMaps() {
    // assert Thread.holdsLock(assertionLock);

    classAssertionStatus = new HashMap<>();
    packageAssertionStatus = new HashMap<>();
    AssertionStatusDirectives directives = retrieveDirectives();

    for(int i = 0; i < directives.classes.length; i++)
        classAssertionStatus.put(directives.classes[i],
                                 directives.classEnabled[i]);

    for(int i = 0; i < directives.packages.length; i++)
        packageAssertionStatus.put(directives.packages[i],
                                   directives.packageEnabled[i]);

    defaultAssertionStatus = directives.deflt;
}

// 从VM检索断言指令。
private static native AssertionStatusDirectives retrieveDirectives();
}


class SystemClassLoaderAction
    implements PrivilegedExceptionAction<ClassLoader> {
    private ClassLoader parent;

    SystemClassLoaderAction(ClassLoader parent) {
        this.parent = parent;
    }

    public ClassLoader run() throws Exception {
        String cls = System.getProperty("java.system.class.loader");
        if (cls == null) {
            return parent;
        }

        Constructor<?> ctor = Class.forName(cls, true, parent)
            .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
        ClassLoader sys = (ClassLoader) ctor.newInstance(
            new Object[] { parent });
        Thread.currentThread().setContextClassLoader(sys);
        return sys;
    }
}
Android解析ClassLoader(一)Java中的ClassLoader 热修复和插件化是目前比较热门的技术,要想更好的掌握它们需要了解ClassLoader,因此也就有了本系列的产生,这一篇我们先来学习Java中的ClassLoader。 阅读详情

相关推荐

Java软件授权机制逆向实战:从源码分析到激活逻辑解析

软件授权机制是保护知识产权的重要技术手段,其核心原理通常涉及加密算法、本地验证与服务器校验相结合的架构。从技术实现角度看,常见的授权验证流程包括机器指纹生成、激活码加密解密、数字签名校验等环节,这些技术广泛应用于各类商业软件中,以确保只有合法用户才能使用高级功能。理解这些机制不仅有助于提升软件安全防护意识,也能深化对加密算法和系统底层交互的认识。在工程实践中,通过分析授权验证流程,开发者可以学习到如何设计健壮的许可系统,或进行合法的兼容性开发。本次探索以具体工具为例,深入剖析了其激活码的生成与验证逻辑,涉及

weixin_34049948的博客 363

Java ClassLoader

一、什么是ClassLoader 顾名思义,即是类加载器,具体作用就是将.class文件加载到JVM虚拟机中去,程序就可以正确运行了。 二、认识.class文件 Java是解释性语言,编写的代码都是.java文件,需要经过compile(编译)成.class文件才能运行。拿入门Hello World来讲: HelloWorld.java public class HelloWorld{ ...

假装你是大灰狼的专栏 13万+

一看你就懂,超详细java中的ClassLoader详解

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的...

frank 的专栏 34万+

超详细java中的ClassLoader详解

ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。 理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。 但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载。 想想也是的,一次性...

dw147258dw的专栏 6697

Simulink仿真避坑指南:如何设置步长、powergui和模块采样时间才能让控制周期更稳定

本文深入解析Simulink仿真中步长、powergui和模块采样时间的协同配置方法,帮助工程师解决控制周期不稳定问题。通过实际案例和参数调优技巧,指导用户精准设置仿真参数,确保电机控制、电力电子系统等工业仿真结果的可靠性。

weixin_42524762的博客 419

# 芯片良率是怎么算出来的?——从泊松到负二项分布,一个芯片测试工程师的视角

- 良率=好Die/总Die,但选错模型可能差17个百分点 - Poisson模型过于悲观,Murphy居中,**Negative Binomial才是业界标配** - 关键不是良率数字,是**跟踪聚类因子α的变化**——它能早几周预警工艺漂移 - Chiplet时代,10颗90%良率的Die封在一起,整体良率只有35%

Freezeblur的博客 655

Java原理示例源码详解系列文档总结

Java原理示例源码详解系列文档总结

wang2leee的博客 715

谷粒商城 - 个人笔记(集群部署篇一)

电商后台管理系统+电商系统,掌握微服务的全套方案。该系统有商品服务、仓储服务、订单服务、购物车服务、检索服务、认证中心服务、网关服务、用户服务、秒杀服务、第三方服务。使用SpringBoot+SpringCloud并配套SpringCloud Alibaba系列,引入全套微服务治理方案:Nacos注册中心/配置中心、Feign远程调用、Gateway网关、Redisson缓存、基于ElasticSearch7全文检索、异步编排与线程池、RabbitMQ柔性事务方案、Redisson分布式锁、分布式信号量等。

知行 4008

getResource(String name)用法及源码分析

Project获取资源需要一个启点,加载资源的动作是由ClassLoader来完成的。 Class对象和当前线程对象可以找到当前加载资源的ClassLoader, 通过ClassLoader的getResource(String name)方法及其它衍生出来的方法可以找到Application的启点及获取相关资源 private void display() { path=this.getClas...

qq_35201262的博客 324

RK3568适配MIPI屏幕触摸,触摸延时很大

我用的是系统,接了一个,设备树已经正确配置,evtest测试坐标值和触摸位置都是准确的。但是出现了一个:我在屏幕上点一下,要等大约 2 秒才能在evtest里看到坐标事件打印。这种现象在 Goodix 系列触摸驱动里比较少见,一般意味着。

小灰灰的博客 1363

Fiori Elements Analytical List Page 标题区配置全攻略:从变体、过滤模式到 UI.KPI 的工程化落地

ALP标题区是Fiori Elements Analytical List Page的核心交互区域,承担变体管理、过滤模式切换、KPI展示等关键功能。本文系统解析了标题区的组成结构、可用能力及工程实现要点,包括:1)标题区包含过滤类型切换、全局KPI、变体管理等交互元素;2)详细说明视觉过滤与紧凑过滤的同步规则及实现方式;3)介绍OData V2下通过UI.KPI注解配置标题区KPI的方法;4)提供manifest.json配置示例和CDS注解写法。文章还对比了V2与V4版本的差异,为ALP开发提供实用指导

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 246

Android全栈知识体系:从JVM底层原理到架构师进阶的完整学习路径

在移动开发领域,构建系统化的知识体系比盲目收集资料更重要。Android应用运行在ART虚拟机之上,无论是Java还是Kotlin,最终都编译为字节码执行,因此理解JVM内存模型、垃圾回收与类加载机制,是掌握底层原理的基石。从Handler消息机制到Binder跨进程通信,再到四大组件启动流程,这些核心模块共同构成了Android系统运行的骨架,也是性能优化与架构设计的技术支撑。对于志在架构师方向的开发者,还需深入Gradle构建原理、插件化与热修复机制,并通过启动优化、内存治理等实战场景,将零散知识点串联

weixin_29182481的博客 218

Windows Docker + RK3588 部署 YOLO26-Pose 人体姿态检测

YOLO26-Pose 在 RK3588 上的部署流程是 Windows Docker 容器内 ONNX 导出 → RKNN 转换(INT8 量化)→ ADB 推送 → 板端 C++ 推理。YOLO26n-Pose 在 640 输入下全链路约 55 FPS,可以覆盖实时姿态检测需求。先在板端用单张图片验证关键点输出正确性,确认 C++ 后处理的解析逻辑无误后,再切换到摄像头实时推理。

m0_64661770的博客 98

10 分钟跑通 OpCore Simplify 构建可引导 OpenCore EFI 完整指南

手工调 `config.plist` 调了白天下午,重启还是卡在开机画面。OpCore Simplify 反过来做:读一份 Windows 硬件报告,自动配好 ACPI 补丁、kext 和 SMBIOS 机型,一键产出可直接拷进启动盘的完整 OpenCore EFI。按下面的流程走,从导入报告到出 EFI 产物约 10 分钟,全程不用翻文档逐个对参数。 ## 🔍 跑之前先确认环境能不能跑 |

gitblog_00897的博客 437

3060台式机 Ubuntu 双系统部署 LingBot-Map 完整指南

摘要: 本文详细介绍了在配备RTX 3060显卡的台式机上部署Ubuntu双系统并安装LingBot-Map 3D重建模型的完整流程。LingBot-Map作为蚂蚁灵波科技开源的Transformer架构模型,在流式3D场景重建中展现出高效性能(20 FPS)和卓越精度(ATE仅6.42米)。指南涵盖关键准备步骤、UEFI分区原理、Ubuntu安装流程(推荐150GB空间及手动分区方案),以及后续驱动配置说明,特别强调RTX 3060需12GB显存支持,并提供了数据备份、Secure Boot处理等实用建议

huanghm88的专栏 310

MATLAB - 浮动基座机器人的逆运动学

本例演示如何解决以浮动底座为模型的机器人的逆运动学问题。浮动底座机器人可以在空间中自由平移和旋转,具有六个自由度。浮动基座机器人的逆运动学问题适用于空间应用,即使用安装在浮动和致动基座上的机械臂在空间操纵物体,也适用于躯干和腿部可在空间自由移动的行走机器人。请注意,刚体树对象表示具有固定基体的刚体树。这意味着刚体树的基体在世界中具有固定的位置和方向。您可以在基体上附加一个 “浮动 ”类型的刚体关节,为具有浮动基体的机器人建模,以用于动态模拟等某些应用。

机器人控制与仿真应用 2440

open-code-review 新手指南:4 条命令从零跑通第一次 AI 代码评审

open-code-review(OCR)是阿里巴巴开源的 **AI 代码评审 CLI**,用"确定性流水线 + LLM Agent"混合架构读取 Git diff,生成行级精准的评审意见,内置 NPE、线程安全、XSS、SQL 注入等多语言规则集,兼容 OpenAI 与 Anthropic 协议。全文按 `安装 → 配置 → 首跑` 的最短路径展开,10 分钟内你能拿到第一条行级评审意见。 [

gitblog_00606的博客 59

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Jetty class loader 类加载过程源码分析初稿

Jetty是个servlet容器,要理解servlet容器的class loader,要先看 JSR315 servlet 3 中对web application class loader的要求,比较重要的地方加了中文解释。 10.7.2 Web Application Class Loader The ...

weixin_34405332的博客 72

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Android中的设计模式

中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一类设计模式:J2EE 设计模式。根据设计模式的参考书。

lhm1124的博客 1143

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

第05讲:深入理解 ClassLoader 的加载机制

深入理解 ClassLoader 的加载机制

ChinaDragon10的博客 3741

类加载器ClassLoader详解

类加载器ClassLoader详解

勇心在馨 8278

Java中类ClassLoader(加载器)全解

Java中类ClassLoader(加载器)全解。引导类加载器、扩展类加载器、系统类加载器负责加载范围详解;ClassLoader源码解析;双亲委派模型流程详解、优势和劣势详解。

全栈行动派的博客 1万+

深入理解Java虚拟机——再谈类的加载器——第十二章——中篇

文章目录1:概述1.1 类加载器分类1.2 类加载器的必要性1.3 命名空间1.4 类加载器的基本特性2:类的加载器分类2.1 引导类加载器2.2 扩展类加载器2.3 系统类加载器2.4 用户自定义类加载器3:测试不同的类加载器4:ClassLoader源码解析4.1 ClassLoader的主要方法4.2 SourceClassLoader与URLClassLoader4.3 ExtClassLoader和AppClassLoader4.4 Class.forName()与ClassLoader.load

qq_44891295的博客 1678

ClassLoader详解(史上最全)

1.类的加载过程 2.类的加载器的作用 3.类的加载器的分类 4.Java类编译、运行的执行的流程 5.使用Classloader加载src目录下的配置文件 @Test public void test2() throws Exception { Properties pros = new Properties(); //此时的文件默认在当前的module下。 //读取配置文件的方式一: // FileInputStream

weixin_43732424的博客 1万+

Java源码解读篇之ClassLoader

源码层面了解下ClassLoader

极客星云-优快云博客 1613
上一篇: 【flink 床头书系列】data Types 数据类型词典
下一篇: 【Java原理系列】 Java中URL原理用法示例中文源码分析
BigDataMLApplication
博客等级 码龄11年 7363粉丝 565原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值