java类加载机制⾯试题_Java类加载器⾯试题
Java类加载器⾯试题
1、Java的类加载器的种类都有哪些?
● 根类加载器(Bootstrap)--C++写的,看不到源码;
● 扩展类加载器(Extension)--加载位置:jre\lib\ext中;
● 系统(应⽤)类加载器(System\App) --加载位置:classpath中;
● ⾃定义加载器(必须继承ClassLoader)。
2、类什么时候被初始化?
● 创建类的实例,也就是new⼀个对象
● 访问某个类或接⼝的静态变量,或者对该静态变量赋值
● 调⽤类的静态⽅法
● 反射(Class.forName("com.lyj.load"))
● 初始化⼀个类的⼦类(会⾸先初始化⼦类的⽗类)
● JVM启动时标明的启动类,即⽂件名和类名相同的那个类只有这6中情况才会导致类的类的初始化。
● 类的初始化步骤:
如果这个类还没有被加载和链接,那先进⾏加载和链接;
假如这个类存在直接⽗类,并且这个类还没有被初始化(注意:在⼀个类加载器中,类只能初始化⼀次),那就初始化直接的⽗类(不适⽤于接⼝);
加⼊类中存在初始化语句(如static变量和static块),那就依次执⾏这些初始化语句。
3、Java类加载体系之ClassLoader双亲委托机制
java是⼀种类型安全的语⾔,它有四类称为安全沙箱机制的安全机制来保证语⾔的安全性,这四类安全沙箱分别是:
1.类加载体系
2.class⽂件检验器
3.内置于Java虚拟机(及语⾔)的安全特性
4.安全管理器及Java API
● 主要讲解类的加载体系:
java程序中的.java⽂件编译完会⽣成.class⽂件,⽽.class⽂件就是通过被称为类加载器的ClassLoader加载的,⽽ClassLoder在加载过程中会使⽤“双亲委派机制”来加载.class⽂件,图:
BootStrapClassLoader:启动类加载器,该ClassLoader是jvm在启动时创建的,⽤于加载$JAVA_HOME$/jre/lib下⾯的类库(或者通过参数-Xbootclasspath指定)。由于启动类加载器涉及到虚拟机本地实现细节,开发者⽆法直接获取到启动类加载器的引⽤,所以不能直接通过引⽤进⾏操作。
ExtClassLoader:扩展类加载器,该ClassLoader是在sun.misc.Launcher⾥作为⼀个内部类ExtClassLoader定义的(即
sun.misc.Launcher$ExtClassLoader),ExtClassLoader会加载$JAVA_HOME/jre/lib/ext下的类库(或者通过参数-dirs指定)。
AppClassLoader:应⽤程序类加载器,该ClassLoader同样是在sun.misc.Launcher⾥作为⼀个内部类,AppClassLoader定义的(即sun.misc.Launcher$AppClassLoader),AppClassLoader会加载java环境变量CLASSPATH所指定的路径下的类库,⽽CLASSPATH 所指定的路径可以通过Property("java.class.path")获取;当然,该变量也可以覆盖,可以使⽤参数-cp,例如:java-cp路径(可以指定要执⾏的class⽬录)。
CustomClassLoader:⾃定义类加载器,该ClassLoader是指我们⾃定义的ClassLoader,⽐如tomcat的StandardClassLoader属于这⼀类;当然,⼤部分情况下使⽤AppClassLoader就⾜够了。
前⾯谈到了ClassLoader的⼏类加载器,⽽ClassLoader使⽤双亲委派机制来加载class⽂件的。ClassLoader的双亲委派机制是这样的(这⾥先忽略掉⾃定义类加载器CustomClassLoader):
1.当AppClassLoader加载⼀个class时,它⾸先不会⾃⼰去尝试加载这个类,⽽是把类加载请求委派给⽗类加载器ExtClassLoader去完成。
2.当ExtClassLoader加载⼀个class时,它⾸先也不会⾃⼰去尝试加载这个类,⽽是把类加载请求委派给BootStrapClassLoader去完成。
3.如果BootStrapClassLoader加载失败(例如在$JAVA_HOME$/jre/lib⾥未查到该class),会使⽤ExtClassLoader来尝试加载;
4.若ExtClassLoader也加载失败,则会使⽤AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
下⾯贴下ClassLoader的loadClass(String name,boolean resolve)的源码:
protected synchronized Class> loadClass(String name,boolean resolve)throws ClassNotFoundException{
// ⾸先缓存是否有 class
Class c=findLoadedClass(name);
if(c==null){
//没有判断有没有⽗类
try{
if(parent!=null){
//有的话,⽤⽗类递归获取 class
c=parent.loadClass(name,false);
}else{
//没有⽗类。通过这个⽅法来加载
c=findBootstrapClassOrNull(name);}
}catch(ClassNotFoundException e){
/
/ ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if(c==null){
// 如果还是没有到,调⽤ findClass(name)去这个类
c=findClass(name);}
}
if(resolve){
resolveClass(c);}
return c;
}
代码很明朗:
⾸先缓存(findLoadedClass),没有的话就判断有没有parent,有的话就⽤parent来递归的loadClass,然⽽ExtClassLoader并没有设置parent,则会通过findBootstrapClassOrNull来加载class,⽽findBootstrapClassOrNull则会通过JNI⽅法”private native Class findBootstrapClass(String name)”来使⽤BootStrapClassLoader来加载class。
如果parent未到class,则会调⽤findClass来加载class,findClass是⼀个protected的空⽅法,可以覆盖它以便⾃定义class加载过程。另外,虽然ClassLoader加载类是使⽤loadClass⽅法,但是⿎励⽤ClassLoader的⼦类重写findClass(String),⽽不是重写loadClass,这样就不会覆盖了类加载默认的双亲委派机制。
双亲委派托机制为什么安全:
举个例⼦,ClassLoader加载的class⽂件来源很多,⽐如编译器编译⽣成的class、或者⽹络下载的字节码。⽽⼀些来源的class⽂件是不可靠的,⽐如我可以⾃定义⼀个java.lang.Integer类来覆盖jdk中默认的Integer类,例如下⾯这样:
package java.lang;
public class Integer {
public Integer(int value) {
}
}
初始化这个Integer的构造器是会退出JVM,破坏应⽤程序的正常进⾏,如果使⽤双亲委派机制的话该Integer类永远不会被调⽤,以为委托BootStrapClassLoader加载后会加载JDK中的Integer类⽽不会加载⾃定义的这个,可以看下下⾯这测试个⽤例:
public static void args){
Integer i=new Integer(1);
}
执⾏时JVM并未在new Integer(1)时退出,说明未使⽤⾃定义的Integer,于是就保证了安全性。
为什么使用bootstrap?
4、描述⼀下 JVM 加载 class?
JVM中类的装载是由类加载器(ClassLoader)和它的⼦类来实现的,Java中的类加载器是⼀个重要的Java运⾏时系统组件,它负责在运⾏时查和装⼊类⽂件中的类。
1.由于Java的跨平台性,经过编译的Java源程序并不是⼀个可执⾏程序,⽽是⼀个或多个类⽂件。
2.当Java程序需要使⽤某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class⽂件中的数据读⼊到内存中,通常是创建⼀个字节数组读⼊.class⽂件,然后产⽣与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可⽤。
3.当类被加载后就进⼊连接阶段,这⼀阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引⽤替换为直接引⽤)三个步骤。最后JVM对类进⾏初始化,包括:如果类存在直接的⽗类并且这个类还没有被初始化,那么就先初始化⽗类;如果类中存在初始化语句,就依次执⾏这些初始化语句。
4.类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和⽤户⾃定义类加载器(java.lang.ClassLoader的⼦类)。
5.从Java 2(JDK 1.2)开始,类加载过程采取了⽗亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM⾃带的Bootstrap是根加载器,其他的加载器都有且仅有⼀个⽗类加载器。类的加载⾸先请求⽗类加载器加载,⽗类加载器⽆能为⼒时才由其⼦类加载器⾃⾏加载。JVM不会向Java程序提供对Bootstrap的引⽤。
● 下⾯是关于⼏个类加载器的说明:
1.Bootstrap:⼀般⽤本地代码实现,负责加载JVM基础核⼼类库(rt.jar);
2.Extension:从dirs系统属性所指定的⽬录中加载类库,它的⽗加载器是Bootstrap;
3.System:⼜叫应⽤类加载器,其⽗类是Extension。它是应⽤最⼴泛的类加载器。它从环境变量classpath或者系统属性java.class.path 所指定的⽬录中记载类,是⽤户⾃定义加载器的默认⽗加载器。
5、获得⼀个类对象有哪些⽅式?
● 类型.class,例如:String.class;
● 对象.getClass(),例如:”hello”.getClass();
● Class.forName(),例如:Class.forName(“java.lang.String”);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。