java文件在解释成class文件之后,需要对里面的类进行初始化,初始化的过程包括加载、连接、装入三个过程,在加载的过程中会对类里面的静态变量进行一个初始化。下面详细介绍下ClassLoader: 1、类的加载、连接和初始化 类初始化通常包括加载、连接、初始化三个步骤。 (1)进程的结束 每当运行一个java程序时,将会启动一个java虚拟机进程,不管程序多么复杂,有多少线程,都在这个java虚拟机进程里。以下四种情况会使得该进程被终止—— 程序运行到最后正常结束; 程序里遭遇了System.exit(),或者是Runtime.getRunTime().exit()代码;程序执行中遇到了未捕获的异常或者错误; java所在平台强制结束了JVM进程; 当该进程结束,那么该进程在内存中的状态将会丢失,包括静态变量的值。 (2)类的加载 类的加载是指将类的class文件读入内存,并为之创建一个java.lang.class对象。 (3)类的连接 类的连接负责把类的二进制数据合并到JRE中,分为三个阶段—— 验证:检验被加载的类是否有正确的内部结构; 准备:负责为类的静态Field分配内存,并设置默认初始值;解析:将类的二进制数据中的符号应用替换成直接引用。 (4)类的初始化 类的初始化,主要就是对静态Field进行初始化。 2、类加载器 (1)类加载器简介 类加载器负责加载所有的类,系统为所有载入内存里的类都会生成一个java.lang.class对象; 同一个类只会被加载一次,在JVM中每一个不同的类都会有一个不同的类加载器负责。 类加载器在不包括用户自定义的加载器的情况下包括三层: Bootstrap Classloader根加载器; ExtensionClassloader扩展类加载器; SystemClassLoader系统类加载器; ①根加载器,又称为引导或者原始加载器,负责加载java的核心类; ②扩展类加载器,负责加载JRE的扩展目录中JAR包的类; ③系统类加载器,又称为应用加载器, 负责JVM启动时加载来自java命令的-classpath或者CLASSPATH环境变量所指定的JAR包和类路径。一般是程序运行的当前路径。所以其称为应用类加载器。 三个类加载器的顺序是: bootstrap classloader | extension classloader | system classloader 如果有用户自定义的类加载器,那么在系统加载器后,将会执行用户类加载器。 (2)自定义类加载器 如果我们需要在启动类加载时作一些特定需求的行为,那么就需要自定义类加载器了。 自定义ClassLoader需要继承ClassLoader抽象类,重写findClass方法,这个方法定义了ClassLoader查找class的方式。 主要可以扩展的方法有: findClass 定义查找Class的方式 defineClass 将类文件字节码加载为jvm中的class findResource 定义查找资源的方式 (3)类加载机制 JVM的类加载机制有以下三种: 全盘负责:所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入。 父类委托:所谓父类委托,就是先让父类加载器先尝试加载该Class,当父类无法加载的时候,才是尝试从自己的类路径中去加载。JVM的ClassLoader采用的是树形结构,除了BootstrapClassLoader以外,每个ClassLoader都会有一个parentClassLoader,即父类加载器,用户自定义的ClassLoader默认的parendClassLoader是SystemClassLoader,当然你可以自己指定需要用哪一个ClassLoader的实例 缓存机制:所谓缓存机制就是保证所有加载过的类都会被缓存,当程序中需要某个类时,会先从缓存区中搜查该类,当缓存区不存在该类对象时,系统才会读取该类的二进制文件。 4、一些重要的方法 (1)loadClass方法 ClassLoader.loadClass()是ClassLoader的入口点。该方法的定义如下: Class loadClass(String name,boolean resolve); name是加载的类的名称,resolve是告诉方法是不中需要解析类PS:并不是所有的类都需要解析,如果JVM只想知道这个类是否存在或找出该类的超类,那么就不需要解析该类 (2)defineClass方法 defineClass方法接受由原始字节组成的数组,并把它转换成Class的对象。原始数组包含如从文件系统或网络装入的数据。defineClass管理JVM的许多复杂的实现层面——它把字节码分析成运行时数据结构、校验有效性等,因为defineClass方法被标记成final的,所以不能覆盖它。 (3)findSytemClass方法 findSystemClass方法就是查找本的类Class文件,然后装入 (4)resolveClass方法 我们在调用编写自己的loadClass方法的时候可以调用resolveClass方法来获得resolve参数 (5)findLoadedClass方法 在调用loadClass方法之前可以调用改方法来查看地ClassLoader是否已经装入了这个类,这样可以避免重新装入这个类 (6)findClass方法 在loadClass默认实现调用这个新方法。findClass的用途包含classLoader的所有特殊代码,而无须复制其他代码 (7)getSystemClassLoader方法 在如果覆盖findClass或loadClass,getSystemClassLoader能以实际的ClassLoader对象访问系统ClassLoader(而不是固定地从findSystemClass调用它)。为了将类请求委托给父类ClassLoader,这个新方法允许ClassLoader获取它的父类ClassLoader.当使用特殊方法,定制的ClassLoader不能找到类时,可以使用这种方法。 父类ClassLoader被定义成创建该ClassLoader所包含代码的对象的ClassLoader. (8)forName方法 在Class类中有一个静态方法forName,这个方法和ClassLoader中的loaderClass方法的目的是一样的,都是用来加载Class的,但是两者在作用上却有所区别: loadClass加载实际上就是加载的时候并不对该类进行解释,因此不会初始化该类。而Class类的forName方法则相反,使用forName加载的时候就会将Class进行解释和初始化
|