一、什么是类加载机制:
将类的.class文件中的二进制数据读到内存中,并将其放入方法区中,在堆中创建一个java.lang.Class对象,这个对象用来封装方法区内的数据。
加载.class文件的方式:
1、本地系统中直接加载
2、将java源文件动态编译为.class文件
3、从专有库中提取.class文件
4、从zip、jar等文件中加载.class文件
5、从网络下载.class文件
二、类的生命周期:
类加载的过程包括了 加载、验证、准备、解析、初始化五个阶段。
加载(获取类的二进制字节流的动作):
加载需要做的三件事:
1、通过一个类的全限定名来获取其定义的二进制字节流
2、将这个字节流的静态存储结构转换为方法区的运行时数据结构
3、在堆生成一个代表这个类的java.lang.Class对象作为方法区中这些数据的访问入口
连接(分为验证、准备、解析)
验证:确保被加载的类的正确性(重要但不是必须的,-Xverifynone参数来关闭大部分的类验证措施)
文件格式验证:验证字节流是否符合Class文件格式的规范
元数据验证:对字节码描述的信息进行语义分析
字节码验证:通过数据流和控制流分析,确保程序语义是合法且符合逻辑的
符号引用验证:确保解析动作能正确执行
准备:为类的静态变量分配内存(都在方法区分配),并将其初始化为默认值
这个时候进行内存分配的仅包括类变量(static),不包括实例变量,实例变量会在对象实例化时随着对象一起分配在java堆中-------->什么是类变量和实例变量?什么是局部变量和全局变量?
类变量:也叫静态变量,在类前加了static的变量
实例变量:也叫对象变量,在类前没有加static的变量
类变量是针对所有对象共有的,一个对象改变其值后,其他对象得到的是改变后的值;实例变量属于对象私有,一个对象改变其值后,不影响其他对象。
全局变量:也叫成员变量,在类中定义的变量,不需要先赋值,系统会赋默认值
局部变量:在方法中定义的变量,使用前需要先赋值,否则编译不通过
针对同时被static和final修饰的常量,必须在声明时就为其显示赋值,否则编译不通过;而只被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。
这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。
解析:把类中的符号引用转换为直接引用
符号引用:一组符号来描述目标,可以是任何字面量
直接引用:直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
初始化:为类的静态变量赋予正确的初始值,主要有两种方式
1、声明变量时指定初值
2、使用静态代码块为类变量指定初值
JVM初始化步骤:
假设这个类还没有被加载和连接,则程序先加载并连接该类
假设该类的直接父类还没有被初始化,则先初始化其直接父类
假设类中有初始化语句,则系统依次执行这些初始化语句
类初始化的时机:
创建类的实例,即new的方式
访问某个类或接口的静态变量,或者对该静态变量赋值
调用类的静态方法
反射
初始化某个类的子类,则其父类也会被初始化
java虚拟机启动时被标明为启动类的类(java Test),直接使用java.exe命令来运行某个主类
三、三类类加载器
启动类加载器:Bootstrap Classloader,负责加载放在JDK\jre\lib(JDK表示JDK的安装目录)目录下,或者被-Xbootclasspath参数指定路径中的能被虚拟机识别的类库(所有java.开头的都能被启动类加载器加载)
扩展类加载器:Extension Classloader,负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的所有类库(如javax.开头的类)
应用程序加载器:Application Classloader,负责加载用户路径(ClassPath)所指定的类
四、类的加载
三种类加载方式:
1、命令行启动应用时由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
Class.forName()与ClassLoader.loadClass()的区别
Class.forName()会将类的.class文件加载到jvm中,同时对类进行解释,执行类中的static块;ClassLoader.loadClass()不会执行static块。
五、双亲委派模式
一个加载器收到了类加载的请求,它首先不会自己去加载该类,而是把这个类请求父加载器去加载,依次向上,当父加载器无法加载时,子加载器才会去加载。
eg:
1、当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
2、当 ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
3、如果 BootStrapClassLoader加载失败(例如在 $JAVA_HOME/jre/lib里未查找到该class),会使用 ExtClassLoader来尝试加载;
4、若ExtClassLoader也加载失败,则会使用 AppClassLoader来加载,如果 AppClassLoader也加载失败,则会报出异常 ClassNotFoundException。