一、程序是如何跑起来的
我们编写的Java文件并不会直接被JVM加载,而是先通过Javac命令编译成class文件后,才能够被JVM加载,加载后通过我们代码中的信息去给对象,类信息等赋予空间去保存,还会有执行引擎去按照我们的代码去执行,程序计数器去记录代码执行的行标数,并有垃圾回收器不时的去回收不再使用的对象
二、类的加载过程
一个类在进入JVM会经历以下过程:加载→校验→准备→解析→初始化
校验
校验是对被加载进来的class文件做一次检查,例如,去检查字节码是否符合规范,保证JVM可以正常运行该class文件
准备
准备就是给被加载的类开辟空间,并且给类对象(也就是static定义的变量)分配空间,并且赋值初始值,例如:
如上图类,在准备阶段,会给类变量 i 分配空间,并赋值为0,注意!,此处不是赋值为3,这里的初始值指的是默认初始值,该0的为0,该为null的为null
解析
解析阶段是将对象变量变成物理地址,JVM并不认识我们的变量名,如果不变成物理地址的话,JVM就不能进行操作
初始化
这里的初始化,才是用我们创建变量时给的初始值去进行赋值,例如上图中给静态变量赋值为3的操作,是在这个阶段去进行赋值的,注意!在此处涉及到对象的初始化,会首先去完成该对象所属类的加载→校验→准备→解析→初始化操作,如下图
在Start类的初始化阶段去创建了User类的实例,所以在此时也会触发对User类的加载等操作。
下图用一张图来说明以上过程
加载
我们写的程序中的类并不是一次性被全部加载的,只有用到时才会被加载,main()是程序的入口,所以,main()方法所在的类会第一时间被加载
如上图代码中,main()方法在Start类中,所以Start类首先被加载,并执行校验→准备→解析→初始化,在初始化阶段我们发现该类main()方法中创建了Cat类的实例对象,所以,Cat类也会被加载
若Cat有父类,如下图:
那么,在加载Cat类之前会首先完成对其父类Animal类的加载,如下是以图片来展示这一流程
类加载器
Java有如下类加载器
启动类加载器 (Bootstrap ClassLoader):加载jdk中lib目录下的类,此处为核心类库
扩展类加载器(Extension ClassLoader):加载lib\ext目录下的类
系统类加载器(Application ClassLoader):加载自己写的类
自定义类加载器:由自己去实现类加载器
双亲委派机制
当一个类需要被加载时,系统类加载器首先会查看自己是否加载过此类,要是已经加载过了,那么会直接返回,若没加载过,则向上询问父类——扩展类加载器,扩展类加载器也会查看自己是否加载过该类,若加载过,则返回,若没加载过,则向上询问父类——启动类加载器,启动类加载器也会进行同样的检查,若启动类加载器都没有加载过该类,那么启动类加载器会查看是否可以加载该类,若无法加载,则向下询问扩展类加载器,一直询问到系统类加载器都无法加载该类,则会查看自定义的类加载器是否可以加载该类,若所有的类加载器都无法加载该类,那么就会报ClassNotFundException,双亲委派机制如下图:
双亲委派机制的优点:防止重复加载、保证核心类不被破坏