在 Java 中 .java 源文件会先编译成 .classs 字节码文件,然后借由 JVM 虚拟机将 .class 字节码加载进内存,最终程序得到运行。其中.class 加载过程就是由 类加载器(ClassLoader) 来完成的。 
而 Android 与 Java 相似, .java 源文件会先编译成 .dex 文件(.class 的集合),然后 Android 虚拟机( ART虚拟机 和 Dalvik虚拟机)将 .dex 文件加载进内存,最终程序得到运行。dex 加载过程也是由 类加载器(ClassLoader)完成的。 
 
什么是类加载?
凭借一个类的全限定名得到对应的可以描述该类的二进制字节流,并将这些字节流转化为方法区的某种数据结构,生成一个 java.lang.Class 对象作为方法区这个类各种数据访问入口。这个过程就是类加载。
 
类加载过程就是由 ClassLoader 来完成的。
类加载器 每个类加载器都有独立的类名称空间。不同类型的类加载器加载同一个 .class 文件得到的类其实是不一样的。只有被同样的加载器加载加载的情况下,对类的比较才有意义(即 Class 对象的 equals, isAssignableFrom(), isInstance() 以及 instance 关键字所对应的函数或者表达式的返回结果是否有效)
Java中的类加载器 Java 中的类加载器分为三种:
Bootstrap ClassLoader(启动类加载器),加载 Java 中的核心类,在 JVM 中由 C++ 实现,其他的类加载器都是在 Java 层的实现。负责将 %JAVA_HOME\lib% 目录下 和 -Xbootclasspath 参数指定目录下的类加载到 JVM 中Extension ClassLoader(扩展类加载器),加载 %JAVA_HOME%/lib/ext 和 java.ext.dirs 所指定的路径下的类到 JVM 中Application ClassLoader(应用类加载器),加载 %CLASSPATH% 路径下的类。包含了自定义的 ClassLoader 
某个具体的类,该由哪个加载器加载呢,加载原则是什么呢?
其加载原则就是 双亲委派 原则。
注:这里的 双亲委派 其实翻译的不够精确,容易让人误解为有两个父类,实际上 Java 是只能单继承的。英文原文为 Parents Delegate 这里应该理解为 父亲委派 原则。
 
也就是说当某个类加载器要加载某个类时,先委托其父加载器加载,依次会传递至 Bootstrap ClassLoader。父加载器处理不了的,则由其子加载器来处理。
看一下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  abstract  class  ClassLoader      protected  Class<?> loadClass(String name, boolean  resolve) throws  ClassNotFoundException {                  Class<?> c = findLoadedClass(name);         if  (c == null ) {              try  {                 if  (parent != null ) {                      c = parent.loadClass(name, false );                 } else  {                      c = findBootstrapClassOrNull(name);                 }             } catch  (ClassNotFoundException e) {             }             if  (c == null ) {                  c = findClass(name);             }         }         return  c;     } } 
有了 双亲委派 原则,先会让上层类加载器加载,上层的类加载器优先级高。比如基础的类 java.lang.Object 无论是什么类加载器加载,都会传递给最顶层加载器去加载,最终得到的 Object 类都是同样的 Object 类,保证了一致性。如果没有 双亲委派 原则,那么各个类加载器加载的 Object 都不是同一个类,也就违背了前面提到的类加载器特点。
Android 中的类加载器 Android 虚拟机中加载的是 .dex 文件(多个 .class 合并而来),跟 Java 的类加载机制相似也不同。
Android ClassLoader 源码分析 在这里以 Android 9.0 为例进行类加载机制的源码分析,源码地址:/dalvik/system/ 
我们来看一下 Android 源码中对 PathClassLoader 和 DexClassLoader 的注释说明:
1 2 3 4 5 6 7 8 9 public  class  PathClassLoader  extends  BaseDexClassLoader       } 
PathClassLoader 用于加载系统类和应用本身的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  DexClassLoader  extends  BaseDexClassLoader       } 
DexClassLoader 可以加载包含了 .dex 的 .jar 以及 .apk 中的类。DexClassLoader 可以用于加载 App 未被安装的那一部分的 dex。需要注意的是,不要把类优化缓存目录放在外部存储空间来避免注入攻击等不安全隐患。
Android 类加载器构造函数 ClassLoader 构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public  abstract  class  ClassLoader      static  private  class  SystemClassLoader           public  static  ClassLoader loader = ClassLoader.createSystemClassLoader();     }     private  final  ClassLoader parent;     private  static  ClassLoader createSystemClassLoader ()           String classPath = System.getProperty("java.class.path" , "." );         String librarySearchPath = System.getProperty("java.library.path" , "" );         return  new  PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());     }     private  static  Void checkCreateClassLoader ()           return  null ;     }     private  ClassLoader (Void unused, ClassLoader parent)           this .parent = parent;     }     protected  ClassLoader (ClassLoader parent)           this (checkCreateClassLoader(), parent);     }     protected  ClassLoader ()           this (checkCreateClassLoader(), getSystemClassLoader());     }     public  static  ClassLoader getSystemClassLoader ()           return  SystemClassLoader.loader;     } } 
getSystemClassLoader() 得到的是一个 PathClassLoader,它的父类加载器是一个 BootClassLoader。也就是说,系统默认的类加载器是 PathClassLoader。
PathClassLoader 构造函数 1 2 3 4 5 6 7 8 9 public  class  PathClassLoader  extends  BaseDexClassLoader      public  PathClassLoader (String dexPath, ClassLoader parent)           super (dexPath, null , null , parent);     }     public  PathClassLoader (String dexPath, String librarySearchPath, ClassLoader parent)           super (dexPath, null , librarySearchPath, parent);     } } 
PathClassLoader 构造函数很简单,直接调用父类 BaseDexClassLoader 的构造函数。第二个构造参数始终是 null,表示 optimizedDirectory 始终为 null。
BaseDexClassLoader 构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public  class  BaseDexClassLoader  extends  ClassLoader      private  final  DexPathList pathList;          public  BaseDexClassLoader (String dexPath, File optimizedDirectory,              String librarySearchPath, ClassLoader parent)          this (dexPath, optimizedDirectory, librarySearchPath, parent, false );     }     public  BaseDexClassLoader (String dexPath, File optimizedDirectory,              String librarySearchPath, ClassLoader parent, boolean  isTrusted)          super (parent);         this .pathList = new  DexPathList(this , dexPath, librarySearchPath, null , isTrusted);         if  (reporter != null ) {             reportClassLoaderChain();         }     } } 
dexPath:包含 类或者 资源 的 .jar/.apk 路径,如果是多个路径,则用 File.pathSeparator(默认是 :) 来分隔。当然也可以直接传 dex 的路径。optimizedDirectory:在 API 26(Android 8.0)的版本中,它表示 odex(optimized dex) 读写存放目录,如果传 null 则表示使用系统默认的目录来存储。自 Android 8.0 起,这个参数已经被弃用,不再生效,使用系统默认的目录。librarySearchPath:native 库文件存放目录,多个库文件则用 File.pathSeparator(默认是 :) 分隔。parent: 父类加载器isTrusted: 当前加载的 dex 是否受信任,如果受信任则可以访问平台隐藏的API,默认为 false 
BaseDexClassLoader 中有一个 DexPathList 类的 pathList 成员变量,它表示 dexPath 下的 .dex 列表。
DexPathList 构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  final  class  DexPathList       private  static  final  String DEX_SUFFIX = ".dex" ;     private  static  final  String zipSeparator = "!/" ;     private  Element[] dexElements;     NativeLibraryElement[] nativeLibraryPathElements;     public  DexPathList (ClassLoader definingContext, String dexPath,              String librarySearchPath, File optimizedDirectory)          this (definingContext, dexPath, librarySearchPath, optimizedDirectory, false );     }     DexPathList(ClassLoader definingContext, String dexPath,             String librarySearchPath, File optimizedDirectory, boolean  isTrusted) {                  this .dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,                                            suppressedExceptions, definingContext, isTrusted);         this .nativeLibraryDirectories = splitPaths(librarySearchPath, false );         this .systemNativeLibraryDirectories =                 splitPaths(System.getProperty("java.library.path" ), true );         List<File> allNativeLibraryDirectories = new  ArrayList<>(nativeLibraryDirectories);         allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);         this .nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);              } } 
类 Element 用来描述一个 dex 文件所代表的元素。字段 dexElements 则为 dex 文件元素列表,通过 makeDexElements() 方法来初始化。
类 NativeLibraryElement 用来描述一个库文件所代表的元素,字段 nativeLibraryPathElements 则为库文件元素列表,通过 makePathElements 方法来初始化。
makeDexEleemnts() 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 private  static  Element[] makeDexElements(List<File> files, File optimizedDirectory,        List<IOException> suppressedExceptions, ClassLoader loader, boolean  isTrusted) {     Element[] elements = new  Element[files.size()];     int  elementsPos = 0 ;          for  (File file : files) {         if  (file.isDirectory()) {             elements[elementsPos++] = new  Element(file);         } else  if  (file.isFile()) {             String name = file.getName();             DexFile dex = null ;             if  (name.endsWith(DEX_SUFFIX)) {                                  try  {                                          dex = loadDexFile(file, optimizedDirectory, loader, elements);                     if  (dex != null ) {                         elements[elementsPos++] = new  Element(dex, null );                     }                 } catch  (IOException suppressed) {                     System.logE("Unable to load dex file: "  + file, suppressed);                     suppressedExceptions.add(suppressed);                 }             } else  {                                  try  {                                          dex = loadDexFile(file, optimizedDirectory, loader, elements);                 } catch  (IOException suppressed) {                     suppressedExceptions.add(suppressed);                 }                 if  (dex == null ) {                     elements[elementsPos++] = new  Element(file);                 } else  {                     elements[elementsPos++] = new  Element(dex, file);                 }             }             if  (dex != null  && isTrusted) {             dex.setTrusted();             }         } else  {             System.logW("ClassLoader referenced unknown path: "  + file);         }     }     if  (elementsPos != elements.length) {         elements = Arrays.copyOf(elements, elementsPos);     }     return  elements; } private  static  DexFile loadDexFile (File file, File optimizedDirectory, ClassLoader loader,                                     Element[] elements)         throws  IOException  {    if  (optimizedDirectory == null ) {         return  new  DexFile(file, loader, elements);     } else  {         String optimizedPath = optimizedPathFor(file, optimizedDirectory);         return  DexFile.loadDex(file.getPath(), optimizedPath, 0 , loader, elements);     } } 
makeDexElements() 就是找到指定文件列表中的所有的 .dex 文件,以数组的形式返回。 loadDexFile 会初始化对应的 DexFile 类,DexFile 代表 .dex 文件。DexFile 在初始化过程中会打开对应的 .dex 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public  final  class  DexFile      private  Object mCookie;     private  Object mInternalCookie;     private  final  String mFileName;     DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws  IOException {         mCookie = openDexFile(fileName, null , 0 , loader, elements);         mInternalCookie = mCookie;         mFileName = fileName;     }          private  static  Object openDexFile (String sourceName, String outputName, int  flags,              ClassLoader loader, DexPathList.Element[] elements)  throws  IOException         return  openDexFileNative(new  File(sourceName).getAbsolutePath(),                                  (outputName == null )                                      ? null                                       : new  File(outputName).getAbsolutePath(),                                  flags,                                  loader,                                  elements);     }          private  static  native  Object openDexFileNative (String sourceName, String outputName, int  flags,              ClassLoader loader, DexPathList.Element[] elements) } 
DexClassLoader 构造函数 1 2 3 4 5 6 public  class  DexClassLoader  extends  BaseDexClassLoader      public  DexClassLoader (String dexPath, String optimizedDirectory,              String librarySearchPath, ClassLoader parent)          super (dexPath, null , librarySearchPath, parent);     } } 
DexClassLoader 跟 PathClassLoader 相似。和前面一样,第二个参数 optimizedDirectory 也从 Android 8.0 开始弃用,不再有效。
类加载器构造函数小结 
BaseDexClassLoader 类(PathClassLoader 和 DexClassLoader)在初始化过程中,会找到其相关的 .dex 列表进行初始化。系统默认的类加载器就是 PathClassLoader 
 
Android 类加载器加载类过程 类加载器加载类的过程,其实就是 ClassLoader 的方法 loadClass()
ClassLoader#loadClass() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public  abstract  class  ClassLoader      public  Class<?> loadClass(String name) throws  ClassNotFoundException {         return  loadClass(name, false );     }     protected  Class<?> loadClass(String name, boolean  resolve) throws  ClassNotFoundException {             Class<?> c = findLoadedClass(name);             if  (c == null ) {                 try  {                     if  (parent != null ) {                         c = parent.loadClass(name, false );                     } else  {                         c = findBootstrapClassOrNull(name);                     }                 } catch  (ClassNotFoundException e) {                 }                 if  (c == null ) {                     c = findClass(name);                 }             }             return  c;     }     protected  Class<?> findClass(String name) throws  ClassNotFoundException {         throw  new  ClassNotFoundException(name);     } } 
我们之前分析过,类加载过程采用的遵循 双亲委派 原则。ClassLoader#findClass() 方法会直接抛出异常,说明 ClassLoader 的子类需要重写该方法才有意义。
BaseDexClassLoader#findClass() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  class  BaseDexClassLoader  extends  ClassLoader      private  final  DexPathList pathList;     @Override      protected  Class<?> findClass(String name) throws  ClassNotFoundException {         List<Throwable> suppressedExceptions = new  ArrayList<Throwable>();         Class c = pathList.findClass(name, suppressedExceptions);         if  (c == null ) {             ClassNotFoundException cnfe = new  ClassNotFoundException(                     "Didn't find class \""  + name + "\" on path: "  + pathList);             for  (Throwable t : suppressedExceptions) {                 cnfe.addSuppressed(t);             }             throw  cnfe;         }         return  c;     } } 
BaseDexClassLoader 实现了 findClass 方法,实际上是在通过 DexPathList#findClass() 方法在 pathList 中找有没有指定的类。
DexPathList#findClass() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26  final  class  DexPathList       private  Element[] dexElements;     public  Class<?> findClass(String name, List<Throwable> suppressed) {         for  (Element element : dexElements) {             Class<?> clazz = element.findClass(name, definingContext, suppressed);             if  (clazz != null ) {                 return  clazz;             }         }         if  (dexElementsSuppressedExceptions != null ) {             suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));         }         return  null ;     }      static  class  Element           private  final  File path;         private  final  DexFile dexFile;                  public  Class<?> findClass(String name, ClassLoader definingContext, List<Throwable> suppressed) {             return  dexFile != null  ? dexFile.loadClassBinaryName(name, definingContext, suppressed)                     : null ;         }     } } 
从上面源码中,可以发现,类加载是在 dexElements 数组中寻找对应的类,一旦在某一个 .dex 文件中找到指定的类,则不再继续查找,并直接返回。
DexFile#loadClassBinaryName() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  final  class  DexFile      public  Class loadClassBinaryName (String name, ClassLoader loader, List<Throwable> suppressed)           return  defineClass(name, loader, mCookie, this , suppressed);     }     private  static  Class defineClass (String name, ClassLoader loader, Object cookie,                                       DexFile dexFile, List<Throwable> suppressed)          Class result = null ;         try  {             result = defineClassNative(name, loader, cookie, dexFile);         } catch  (NoClassDefFoundError e) {             if  (suppressed != null ) {                 suppressed.add(e);             }         } catch  (ClassNotFoundException e) {             if  (suppressed != null ) {                 suppressed.add(e);             }         }         return  result;     }     private  static  native  Class defineClassNative (String name, ClassLoader loader, Object cookie, DexFile dexFile)  } 
最终会调用 native 层的 defineClassNative 方法,来查找 .dex 文件中相应的类。
Android 类加载器加载类过程小结 
Android 类加载过程也遵循 双亲委派 原则 
BaseDexClassLoader 加载类是顺序的,前面的 .dex 中如果能找到指定的类,则后面的不再查找 
总结 
Java 和 Android 类加载过程都遵循 双亲委派 原则 
PathClassLoader 用于加载系统类和应用本身的类。系统默认的类加载器就是 PathClassLoaderDexClassLoader 可以用于加载 App 未被安装的那一部分的 dex。BaseDexClassLoader 加载类是顺序的。