【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

实战指南!基于STM32的智能环境监测系统(代码+硬件设计+云端控制)

本文详细介绍了基于STM32的智能环境监测系统的设计与实现,包括硬件选型、电路设计、软件架构和云端控制。系统通过DHT11温湿度传感器和MQ-2烟雾传感器实时监测环境数据,STM32F103C8T6主控芯片处理数据并通过ESP8266上传至中国移动物联网平台,支持手机APP远程监控。项目成本低、稳定性高,适合智能家居和环境监测应用。

weixin_29194817的博客 433

Docker深度详解:从原理到实践的全方位指南

本文深入解析了Docker容器化技术的核心原理与实战应用。主要内容包括:1) Docker核心架构与优势,对比容器与虚拟机的关键差异;2) 企业级部署方案,涵盖离线安装与多镜像源配置;3) 高级镜像构建技术,如参数化构建、缓存优化及安全扫描;4) 容器生命周期管理,包括健康检查、资源限制及数据持久化方案。文章提供了丰富的代码示例和生产级配置建议,如使用Trivy进行安全扫描、精确控制容器资源等技术细节,帮助开发者全面掌握现代容器化技术。

weixin_42593797的博客 1281

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

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

wang2leee的博客 715

HDRnet——双边滤波和仿射变换的摇身一变

layer层数越多,下采样得到的图越小,low-level features的好处是最后的grid会更coarser,最后的特征图的感受野也越多,非线性更好,表达力更强。所以最终grid是5维的[B,in*out,n,H,W],permute之后是[B,H,W,n,in*out],B是batchsize,n表示对guide的量化级数,in*out表示每个cell的变换系数。这部分的处理对象是低分辨率图,利用CNN进行特征提取(局部和全局),最后fuse得到grid,这里面包含了变换的系数。

zcg1942的博客 1577

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

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

qq_35201262的博客 324

【数据集】GPM IMERG Daily Precipitation Dataset

GPM IMERG Daily Precipitation Dataset

WW、forever的博客 1219

基于YOLOv10的机器人抓取引导系统:从物体检测到精准抓取

python# 数据标注工具简化版import os"""启动标注工具""""""标注单张图像"""print(f"标注图像: {os.path.basename(img_path)}")print("请在图像上绘制抓取矩形框...")# 这里应该实现交互式标注界面# 简化版本直接返回示例标注# 示例标注数据"""保存标注结果"""# 保存为YOLO格式# 转换为YOLO格式。

YOLO 378

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

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

weixin_29182481的博客 218

架构师之HBase vs MongoDB 技术选型与场景决策实战案例

面对海量数据和高并发查询,该选 HBase 还是 MongoDB?本文为你提供完整的决策框架!从数据模型对比、架构解析、六大真实场景(电信 CDR、金融行情、广告点击流、电商商品、社交档案、内容管理)到性能、一致性、运维全面对比,助你在 PB 级时序数据和灵活查询之间做出精准选择!

肖哥弹架构博客 1102

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

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

gitblog_00897的博客 437

手把手教您国内安装 Claude Code 并配置自定义大模型(包含New-API中转和可视化管理工具的使用)

国内免魔法畅用 Claude Code!手把手教你 Windows 安装、配置文件写法、对接阿里百炼 / DeepSeek / 智谱✨,附完整参数、正确命令、三方工具管理方案,新手一次成功!✅

XiaoqiangClub的博客 3785

什么是 SAP PCE?

SAP Private Cloud Edition 是一种基于云计算的部署模式,它结合了公有云的灵活性和私有云的安全性,为企业提供了量身定制的云服务。在 PCE 环境中,SAP 托管和维护客户的应用系统(如 SAP S/4HANA、SAP SuccessFactors 或 SAP BW/4HANA),同时客户拥有更高的定制化权限。专属环境:每个客户在云中拥有独立的资源,这保障了更高的安全性和性能。全面托管:SAP 提供基础架构、系统管理和更新服务,降低了客户的运维成本。灵活扩展。

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

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

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

gitblog_00606的博客 59

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

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

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Android中的设计模式

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

lhm1124的博客 1143

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

第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、付费专栏及课程。

余额充值