②. 抽象类ClassLoader的主要方法(内部没有抽象方法)
①. public final ClassLoader getParent():返回该类加载器的超类加载器
②. public Class<?> loadClass(String name) throws ClassNotFoundException
(加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,则返回ClassNot FoundException 异常。该方法中的逻辑就是双亲委派模式的实现)
③. protected Class<?> findClass (String name) throws ClassNotFoundException
查找二进制名称为name的类,返回结果为java.lang.Class类的实例。这是一个受保护的方法,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass()方法调用。
④. protected final Class<?> defineClass(String name, byte[] b, int off, int len)
根据给定的字节数组b转换为Class的实例,off和len参数表示实际Class信息在byte数组中的位置和长度,其中byte数组b是ClassLoader从外部获取的。这是受保护的方法,只有在自定义ClassLoader子类中可以使用。
⑤. protected final void resolveClass(Class<?> c)
链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。前面我们说链接阶段主要是对字节码进行验证,为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用
⑥. 源码解析Classloader方法
测试代码: ClassLoader.getSystemClassLoader().loadClass("com.xiaozhi.java.User"); //resolve==true,加载class的同时需要进行解析操作 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 { //parent==null 父类加载器是引导类加载器 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } // 当前类的加载器的父类加载器未加载此类 or 当前类的加载器未加载此类 if (c == null) { // 调用当前classloader的findClass long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } //是否进行解析操作 if (resolve) { resolveClass(c); } return c; } }
③. SecureClassLoader与URLClassLoader
①. 接着SecureClassLoader扩展了 ClassLoader,新增了几个与使用相关的代码源(对代码源的位置及其证书的验证)和权限定义类验证(主要指对class源码的访问权限)的方法,一般我们不会直接跟这个类打交道,更多是与它的子类URLClassLoader有所关联
②. 前面说过,ClassLoader是一个抽象类,很多方法是空的没有实现,比如 findClass()、findResource()等。而URLClassLoader这个实现类为这些方法提供了具体的实现。并新增了URLClassPath类协助取得Class字节码流等功能。在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁
④. Class.forName()与ClassLoader.loadClass()对比
①. Class.forName():是一个静态方法,最常用的是Class.forName(String className);根据传入的类的全限定名返回一个 Class 对象。该方法在将 Class 文件加载到内存的同时,会执行类的初始化。如:Class.forName(“com.atguigu.java.HelloWorld”);
②. ClassLoader.loadClass():这是一个实例方法,需要一个 ClassLoader 对象来调用该方法。该方法将 Class 文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。该方法因为需要得到一个 ClassLoader 对象,所以可以根据需要指定使用哪个类加载器。