你真的理解类加载吗?

类加载之类加载器(ClassLoader)

在介绍讲解类加载的过程之前,有必要简单介绍一下类加载实现的核心,那就是类加载器以及Java类加载器的双亲委派机制。主要还是以一张比较经典的模型图片作为讲解,如下:
在这里插入图片描述
从上图可以看出,Java的类加载器主要有四类,分别是用户自定义的,应用程序类的加载器,扩展类加载器以及启动类加载器,那么他们之间的区别和联系是什么呢,什么又是双亲委派?包括类加载器对应的场景又是什么,一下将一一介绍。

User ClassLoader

用户自定义的类加载器,顾名思义,就是根据用户需求自己定义的一个类加载程序,一般需要只需要继承ClassLoader类,覆盖findClass方法。应用场景一般是需要加载指定的.class文件时使用。

Application ClassLoader

这个类加载器其实我们平时在加载类的时候一直有使用,只不过是JVM自动调用了,我们没有仔细注意,它的作用就是加载我们平时写的程序的类的文件,是使用最频繁的一个。

Extension ClassLoader

扩展类加载器主要负责加载 JRE 的扩展目录中JAR包的类,或者由Java.ext.dirs指定。通过这种方式,可以为Java扩展核心类以外的新功能。

BootStrap ClassLoader

启动类加载器是一个顶级父类,主要用于加载rt.jart中的charset.jar等核心类,值得一一提的是,它是由c++实现的,这个类加载程序对象并不真实存在,所有他的返回值类型为null。

查看类加载器的方法

一般通过对象.getClass().getClassLoader()获得一个返回值,通过这个参数便可以得到这个类被加载的对应的类加载器的信息.

什么是双亲委派机制?

双亲委派机制,实际上就是类加载器的选择过程以及是否产生类加载的抉择,当虚拟机遇到一个类时候,由上图讲解过程可以概述为:自定义类加载器自底向上会发出询问信息,查询这个类是否加载过,每个加载器都会记录其加载过的信息,如果已经加载过,就直接使用,不再产生类加载,如果没有加载,会自顶向下依次通知,这个类不存在,可以加载,直到通知到自己所属加载范围的类加载器,就开始类加载的过程。
这个自底向上查询,自顶向下加载的过程,就是类加载的双亲委派机制。

为什么是双亲委派机制?

整体由两个原因,一是为了分类加载,有序管理,效率也相对清晰,第二点就是考虑到安全问题,权限设定,保证了加载信息的安全性。

什么是类加载?

通过以上简单的介绍,相比我们已经对类加载的过程有一定的认识,那么究竟什么是类加载?其实类加载是一个极其复杂的过程,但是整体可以分为五个大的过程,以下还是以图片的方式做介绍。

在这里插入图片描述
Java的加载主要由:
Loading—(加载)
加载的过程,通过上文的描述已经非常清晰了,这就是整个类加载的第一步,通过类加载器将其加载到虚拟机内存中,存放在方法区(关于jvm内存模型,上一篇文章由提及),不过一个**.class**文件加载到内存中也不是一个简单的过程,其中涉及到Java汇编和底层的设计知识,本文不再细细讲述。
Rerification—(校验)
校验,顾名思义,就是检查加载进虚拟机的文件的格式,包括魔数(CAFEBABE)以及版本号,常量池(十六个)等,还会验证元数据信息(字节码信息是否符合Java语言规范)。
Preparation—(准备)
这个过程是为静态变量赋初始值的过程(注意:此过程为静态变量的半初始化状态,此时的值是0不是null,若为常量,则在此过程完成初始化)。
Resolution—(解析)
这个过程是将常量池的相互引用转为实际地址的的过程,以保证其引用是真实存在的数据。
以上三个过程可以被看作一个大的连接过程—Linking
Initializing—(初始化)
此过程是真正执行字节码的过程,也就是触发初始化的过程。这个阶段的静态常量为null。由于Java采用的是懒加载策略,只有当我们需要用到这个类的时候才会去加载他,那么什么情况下会初始化数据呢?

;类加载的五个情况

1.
碰到new,getstatic关键字的时候,如果该类没有被加载,会触发初始化。
2.
使用反射调用相关类的方法时。
3.
创建子类对象导致父类类加载。

4.
虚拟机启动时,包含有main()相关的类

5.
Java1.7在动态语言支持时,解析结果为:REF_get/put/invokestatic相关句柄的时候,对应类没有初始化,也会导致类加载初始化。

值得注意的是:使用已经加载到常量池池中的静态数据不会产生类加载。

public class TestClass {
    public static void main(String[] args) {
        int a =TestClass1.n;
        System.out.println("-----------------------");
        int b =TestClass1.m;

    }
}

class TestClass1{
    public  static int m=20;
    public static  final int n=20;
    static {
        System.out.println("静态代码...");
    }
}

在这里插入图片描述
由代码和运行结果也可以测试得出此结论。

五个主要过程构成,其次还有

Using(使用)和Unload(卸载)两个后续过程,比较容易理解。
使用就是代码真正执行的过程。
卸载的过程就是将创建的类对象销毁,负责运行的JVM退出。
(相关垃圾回收,有时间总结)

其实我们平时运行的主线程main函数也是属于一个类对象的静态方法,由虚拟机直接完成调用的,如果机制设置的是其他名字,那么函数的入口也是可以被修改的,至此,又一次感受到了反射的强大之处,因此理解好类对象的原理,对Java的深入学习真的很有帮助。
以上就是整个文章的介绍了,如有不足之处,欢迎讨论指出。

补充:

总结类加载相关代码块的执行顺序:
1.静态代码块–>构造块–>构造方法–>普通代码块
2.类加载的过程是线程安全的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值