一道面试题
能不能自己写个类叫java.lang.System?
答案:通常不可以,但可以采取另类方法达到这个需求。
解释:为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。
双亲委派模式优势
避免重复加载 + 避免核心类篡改
采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java
API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
JVM预定义的三种类型类加载器:
启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将
<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。标准扩展(Extension)类加载器:是由 Sun 的
ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将<
Java_Runtime_Home >/lib/ext或者由系统变量
java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。系统(System)类加载器:是由 Sun 的
AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。
双亲委派模型工作工程:
1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。
2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap
ClassLoader去完成。3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。
4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。
5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
这就涉及到了类的具体加载过程, 如下图, 类的加载过程被从左到右划分为 3 大阶段:
1.装载 (Loading)
该阶段负责找到待加载类的二进制 class 文件, 并把它以 bytecode 的形式装载到虚拟机。
在这个过程中, JVM 会给这个类分配一个基本的内存结构, 但是方法, 变量域, 和它引用到的其他类在这个阶段都还没有处理, 也就是说,
这个类在目前阶段还不可用
2.链接 (Linking)
这个步骤又可细分为3个阶段
- 字节码验证
验证字节码是否是一个正确,符合规范的类字节码 - 类准备
为这个类定义好必须的数据结构以表示成员变量域, 方法, 以及实现的接口等等 - 解析
把这个类锁引用的其他类全部加载进来 , 引用的方式有如下几种:- 继承
- 实现接口
- 域变量
- 方法定义
- 方法中定义的本地变量
3.初始化(Initializing)
执行类中定义的静态代码块, 初始化静态变量为默认值
隐式加载 vs 显示加载
从上文类加载的详细过程可以看出, 类有两种方式被加载进来
- 显式加载
程序主动调用下列类型的方法去主动加载一个类
- classloader.loadClass( className)
- Class.forName( className)
- 隐式加载
被显式加载的类对其他类可能存在如下引用:
- 继承
- 实现接口
- 域变量
- 方法定义
- 方法中定义的本地变量
- 被引用的类会被动地一并加载至虚拟机, 这种加载方式属于隐式加载
参考:https://blog.csdn.net/lengxiao1993/article/details/86689331
标签:委派,Extension,ClassLoader,System,优点,双亲,加载 From: https://www.cnblogs.com/JavaYuYin/p/17997398