类的生命周期
前面三步:加载 连接 初始化 将Class文件加载到虚拟机中
加载
1、通过全类名定义获取此类的二进制字节流
(全类名即包名 + 类名)
2、将字节流所代表的静态存储结构转换为方法区的运行时数据结构
3、在内存中生成一个代表该类的java.lang.Class对象,作为方法区这些数据的访问入口
总结:加二进制数据到内存 ——> 映射成JVM能够识别的结构 ——> 在内存中生成class文件
每个java类都有一个引用指向加载它的ClassLoder
验证
确保class文件中的字节流包含的信息,符合当前虚拟机的要求
准备
为类中的静态字段分配内存,并设置默认的初始值,如int类型的初始值是0
被final修饰的static字段不会设置,因为final在编译的时候就分配了
解析
虚拟机将常量池的符号引用替换为直接引用的过程
符号引用:
符号引用以一组符号来描述所引用的目标
直接引用:
直接引用是可以直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄
如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,则触发类的加载
初始化:
初始化执行类的构造器方法init()
这个方法不需要定义,是javac编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并来的
比如 private int value = 3
在初始化阶段,value的值将变为3
类初始化时机:当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:
- 创建类的实例
- 访问某个类或接口的静态变量,或者对静态变量赋值
- 调用类的静态方法
- 反射(如Class.forName(“xxxx”))
- 初始化某个类的子类,父类也会被初始化
- java虚拟机启动时被标明为启动类的类,即主类
类卸载:卸载类即该类的Class对象被GC
类加载器
加载java类的字节码(.class文件)到JVM中
字节码可以是.java文件经过javac编译得到 也可以通过工具动态生成、通过网络下载得来
加载规则:先判断该类是否被加载过,已经加载的类直接返回
内置加载器:
1、BootstrapClassLoader
– 启动类加载器
加载JDK内部的核心类库
2、ExtensionClassLoader
– 扩展类加载器
加载ext目录下的jar包和类
3、AppClassLoader
– 应用程序类加载器
负责加载当前应用classPath下的所有jar包和类
自定义类加载器
需要继承java.lang.ClassLoader
关键方法:
1、proteceted Class loadClass(String name,boolen resolve)
加载指定二进制名称的类,实现双亲委派机制
2、protected Class findClass(String name)
根据类的二进制名称来查找类
获取ClassLoader的方式
// 方式一:获取当前类的 ClassLoader
clazz.getClassLoader()
// 方式二:获取当前线程上下文的 ClassLoader
Thread.currentThread().getContextClassLoader()
// 方式三:获取系统的 ClassLoader
ClassLoader.getSystemClassLoader()1
// 方式四:获取调用者的 ClassLoader
DriverManager.getCallerClassLoader()
双亲委派模型
什么是双亲委派模型?
每个ClassLoader实例都有一个相关的父类加载器,ClassLoader实例会在试图亲自查找类或者资源之前,将搜索类或资源的任务委托给其父类加载器
ClassLoader的loadClass()方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//首先,检查该类是否已经加载过
Class c = findLoadedClass(name);
if (c == null) {
//如果 c 为 null,则说明该类没有被加载过
long t0 = System.nanoTime();
try {
if (parent != null) {
//当父类的加载器不为空,则通过父类的loadClass来加载该类
c = parent.loadClass(name, false);
} else {
//当父类的加载器为空,则调用启动类加载器来加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//非空父类的类加载器无法找到相应的类,则抛出异常
}
if (c == null) {
//当父类加载器无法加载时,则调用findClass方法来加载该类
//用户可通过覆写该方法,来自定义类加载器
long t1 = System.nanoTime();
c = findClass(name);
//用于统计类加载器相关的信息
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//对类进行link操作
resolveClass(c);
}
return c;
}
}
双亲委派模型的好处:避免类的重复加载