一、类的加载时机

  1. 实例化对象,也就是new一个对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(Class.forName("com.zlx.load"))
  5. 初始化一个类的子类,其父类没有初始化时,会先初始化其父类
  6. JVM启动时标明的启动类,即文件名和类名相同的那个类    

二、类的生命周期

java什么时候会触发熔断器_java

三、类的加载过程

1. 加载

在加载阶段,虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在 java 堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口

2.验证

  1. 文件格式验证:主要验证字节流是否符合Class文件格式规范,并且能被当前版本的虚拟机处理
  2. 元数据验证:主要对字节码描述的信息进行分析,以保证其符合Java语言规范的要求
  3. 字节码验证:主要确定程序语义是否合法、符合逻辑
  4. 符号引用验证:主要是对类自身以外(常量池中的各种符号引用)的信息进行匹配校验

3.准备

为类或者接口的静态字段分配空间,并设置默认初始值(final 修饰静态变量的在准备阶段赋值,非 final修饰的静态变量初始化时赋值)

public static int value = 123;

value在准备阶段的初始值为0而不是123,只有到了初始化阶段,value才会为123。

Java中所有基础类型的初始值:

数据类型

初始值

int

0

long

0L

short

(short)0

char

\u0000

byte

(byte)0

boolean

false

float

0.0f

double

0.0d

reference(引用类型)

null

 

一种特殊情况是,如果字段属性表中包含常量,那么准备阶段value就会被初始化为所指定的值,比如下面的value如果这样定义:value在准备阶段值就已经是123了

public static final int value = 123;

4.解析

虚拟机将常量池内的符号引用替换为直接引用。 

5.初始化

为类的静态变量赋予正确的初始值到了这一步,才把第三步 123 赋值给 value。

四、类加载器

JVM定义了三种类加载器,当一个JVM启动的时候,Java开始使用以下三种类加载器:

  1. 根类加载器(bootstrap class loader):它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里全部的class,由C++实现,不是ClassLoader子类)。因为引导类加载器涉及到虚拟机本地实现细节,开发者没法直接获取到启动类加载器的引用,因此不容许直接经过引用进行操做。
  2. 扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
  3. 应用类加载器(application class loader):被称为应用类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序能够经过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。若是没有特别指定,则用户自定义的类加载器都以此类加载器做为父加载器。由Java语言实现,父类加载器为ExtClassLoader。

 五、双亲委派机制

工作原理:若是一个类加载器收到了类加载请求,它并不会本身先去加载,而是把这个请求委托给父类的加载器去执行,若是父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,若是父类加载器能够完成类加载任务,就成功返回,假若父类加载器没法完成此加载任务,子加载器才会尝试本身去加载。

优点:采用双亲委派模式的是好处是Java类随着它的类加载器一块儿具有了一种带有优先级的层次关系,经过这种层级关能够避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设经过网络传递一个名为java.lang.Integer的类,经过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样即可以防止核心API库被随意篡改。