双亲委派模式的优点

一道面试题
能不能自己写个类叫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)类加载器:是用本地代码实现的类装入器,它负责将
    /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加载失败(在\lib中未找到所需类),就会让Extension ClassLoader尝试加载。

4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。

5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
双亲委派模式的优点_第1张图片
双亲委派模式的优点_第2张图片

这就涉及到了类的具体加载过程, 如下图, 类的加载过程被从左到右划分为 3 大阶段

1.装载 (Loading)
该阶段负责找到待加载类的二进制 class 文件, 并把它以 bytecode 的形式装载到虚拟机。 在这个过程中, JVM 会给这个类分配一个基本的内存结构, 但是方法, 变量域, 和它引用到的其他类在这个阶段都还没有处理, 也就是说, 这个类在目前阶段还不可用
2.链接 (Linking)
这个步骤又可细分为3个阶段

  • 字节码验证
    验证字节码是否是一个正确,符合规范的类字节码
  • 类准备
    为这个类定义好必须的数据结构以表示成员变量域, 方法, 以及实现的接口等等
  • 解析
    把这个类锁引用的其他类全部加载进来 , 引用的方式有如下几种:
    • 继承
    • 实现接口
    • 域变量
    • 方法定义
    • 方法中定义的本地变量

3.初始化(Initializing)
执行类中定义的静态代码块, 初始化静态变量为默认值

隐式加载 vs 显示加载
从上文类加载的详细过程可以看出, 类有两种方式被加载进来

  1. 显式加载
    程序主动调用下列类型的方法去主动加载一个类
  • classloader.loadClass( className)
  • Class.forName( className)
  1. 隐式加载
    被显式加载的类对其他类可能存在如下引用:
  • 继承
  • 实现接口
  • 域变量
  • 方法定义
  • 方法中定义的本地变量
  • 被引用的类会被动地一并加载至虚拟机, 这种加载方式属于隐式加载

原文:https://blog.csdn.net/lengxiao1993/article/details/86689331

你可能感兴趣的:(Android)