飞越面试官(四)--类加载过程
首先非常感谢@风和日暖 对文章排版给出的干货。
这一章内容比较少,但也足以向大家证明飞越面试官,这事不可能。说起来,我还从来没有被问起这个问题,可能是面试的岗位比较低,其他人倒是有被问过的。既然内容不多,那就宁缺毋滥。据我从面经上来看,通常会问下知不知道类加载过程有哪些步骤?顺口而出,就是加载链接初始化,其中链接还包括验证准备解析。(这个时候最好就顺势展开各个点都做了些什么),加载的话,实现是根据类名获取定义此类的二进制文件流,这个步骤还提供自定义的,然后将二进制流储存所代表的静态储存结构转换成方法区/元空间运行时的数据结构,最后在内存中生成一个代表该类的Class对象,作为方法区/元空间这些数据的访问入口。
那其他过程呢?简单来说,链接的过程包括验证准备解析,验证就是检查class文件的正确性,例如class文件能不能和这个版本的JVM匹配,还有文件格式、字节码和符号引用都要验证。准备呢,就是把字段属性给该置0的置0,该是null的设置null,做一个初始化零值,这里并不是指执行构造函数和静态代码块之类的初始化。解析的话,就是将符号引用转换成直接引用,符号引用的存在是为了在不确定的情况下,给一个占位符做代表,直接引用就是真实的内容了。剩下的初始化就是执行init内容,例如static代码块。
那么类加载器都有哪些呢?自带的就有三个,第一个是BootstrapClassLoader启动类加载器,作为最顶层的加载器,由C++编写,负责加载JAVA_HOME之下的lib目录的jar包和类,或者被-Xbootclasspath参数指定的路径下所有类。第二个是ExtensionClassLoader拓展类加载器,主要负责加载JAVA_HOME之下lib目录下的ext里所有的类,或被java.ext.dirs系统变量所指定的路径下的jar包。第三个是AppClassLoader应用程序类加载器,面向我们用户的加载器,负责加载当前应用classpath下所有的jar包和类。除了这三个加载器,有需要的话还可以继承ClassLoader实现自己的类加载器。
大名鼎鼎的双亲委派模型有没有了解!?必须听过!刚才说了三个类加载器,他们是有层级关系的,这里的双亲其实有点误解,不是说的父母亲这个意思,说的是上下层的关系,就是说,每一个类都有一个对应它的类加载器,加载默认采用的就是双亲委派模型,就是在类加载的时候,系统会首先判断当前类有没有加载过,已经被加载过的类自然被直接返回了,否则的话才会被尝试加载,加载的时候,首先会把这件事委派给上一级类加载器的loadClass方法处理,所以这件事最终也会到达最高层bootstrapClassLoader那里去,当最高层能处理就会自己处理,不能的话,它会返回给下一层让他们自己处理,以此类推,找出一个能加载类的。如果上一层加载器为null,就启动bootstrapClassLoader作为上一层加载器。
它这样搞法有什么好处呢?有好处的,保证了Java程序的稳定运行,可以避免类的重复加载,像JVM区分是不是不同类的方式,就是这些类被不同的类加载器产生。如果不使用双亲委派模型,编写一个诸如java.lang.Object的类,就可能出现不同的Object类。做个比喻,你在家里人随便选一个人出来,看他是不是一个人,而且生下他的也是一个人。
还涉及到了隐式加载和显式加载。隐式加载指的是程序在使用new等方式创建对象,会隐式地调用类加载器把对应的类加载到JVM中。显式加载指的是通过直接调用class.forName方法把所需类加载到JVM中,也就是直接指明了具体类。
第一版Java面试知识点汇总下载(有不少错别字和没更新的):https://pan.baidu.com/s/1MxKXIZtoBd57pTwTIDyrgA 提取码: 3arb。
相关阅读: