做为的学习笔记,类的加载机制主要从类的加载,连接,以及初始化 ,类加载器除了根类加载器之外,其他类加载器都是用java编写的,所以我们程序员可以开发自己的类加载器,通过使用自定义类加载器,可以完成一些特定的功能。
1 jvm 和类的认识
当我们通过java命令运行java程序时,这个命令就会启动一个java虚拟机进程,我们的java程序就处于这个进程中,java程序中的所有线程,所有变量都处于jvm的同一个进程里,它们都使用该jvm进程的内存区。
当系统出现一下情况时,jvm进程将被终止
》程序运行到最后正常结束
》程序所在平台强制结束了jvm进程
》 程序执行过程中遇到未捕获的异常或者错误而结束
》 程序运行到使用System.exit() 或者Runtime.getRuntime().exit()代码处结束程序
java程序结束,jvm进程结束,该进程在内存中的状态将会失去,下面举例子说明:
& 首先创建一个静态变量 q
public class TestQ {
//定义一个静态变量
public static int q = 4;
}
& 定义一个测试类 创建TestQ实例,并访问该实例的静态变量q
public class TestTQ {
public static void main(String[] args) {
TestQ t = new TestQ();
t.q++;
System.out.println(t.q);
}
}
& 创建第二测试类 并访问类变量q
public static void main(String[] args) {
TestQ t2 = new TestQ();
System.out.println(t2.q);
}
}
我们首先运行第一个测试类 访问q 自增了1 程序给出的结果是5 相信大家对这个结果没有什么疑问把,而我们再来运行第二个测试类的结果是多少呢,依然是4 ,为什么呢? 原因是两次运行java程序处于2个不同的jvm进程中,第一次运行jvm结束后,它对q所做的修改将全部失去,两个jvm之间不会共享数据。
2 类的加载
类的加载: 当程序主动使用 一个类时 ,如果该类没有被加载时,则jvm会通过 加载 连接 初始化三个步逐来对类进行初始化操作 ,三个操作一次完成,把这三个步逐统称为类加载或类初始化,类加载就是将类的class文件读入内存,并为之创建一个java.lang.Class对象 。
类的加载又类加载器完成的,类加载器通常是由jvm提供,这些类加载器也是程序运行的基础,类加载器分为2类,一是jvm提供的系统类加载器,另一个是 开发者可以通过继承ClassLoader基类来创建自己的类加载器,
通过使用不同的类加载器。可以从不同来源加载类的二进制数据,通常有如下几种来源:
》 从本地系统文件加载class文件
》从jar包加载class文件
》通过网络加载class 文件
》把一个java 源文件动态编译,并执行加载
类的加载不一定都是在”第一次使用“ 才进行加载,java虚拟机规范允许系统预先加载一些类。
3 类的连接
当类被加载了之后,系统会为之生成一个对应的class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到jre中,而类的连接又分为三个阶段
》 验证: 验证阶段用于检测被加载的类是否又正确的内部结构,并且和其他类协调一致
》 准备 :类准备阶段则负责为类变量分配内存,并设置默认初始值
》 解析:将类的二进制数据中的符号引用替换成直接引用
4 类的初始化
虚拟机负责对类进行初始化,主要就是对类变量进行初始化,在java类中对类变量指定初始值有2种方式:1是 声明变量时指定初始值 2是 使用静态初始化块为类变量指定初始值 静态初始化块都将被当成类的初始化语句,jvm会按照这些语句在程序中的排序顺序依次执行它们。
public class Test01 {
static{
int b = 8;
System.out.println("-------");
}
static int a = 3;
static int b = 12;
static int c;
public static void main(String[] args) {
System.out.println(Test01.b);
System.out.println(Test01.c);
}
}
其中b的值为12 ,c 的值为0 ,b在第二次赋值会把其中的值替换掉,没有赋予值的int 类型,默认值为0。
jvm 初始化一个类包含如下几个步逐
》 如果这个类还没有被加载和连接。则程序先加载并且连接该类
》 如果该类的直接父类还没有被初始化,则先初始化其直接父类(jvm 最先初始化的总是java.lang.Object类)
》如果类中有初始化语句,则系统一次执行这些初始化语句
当程序主动使用一个类时,系统会保证该类以及所用父类都会被初始化。
5 类初始化的时机
》 调用一个类的静态方法
》访问一个类或接口的类变量,或为类变量赋值
》 使用反射方式来强制创建一个类或者接口对应的java.lang.Class对象 (Class.forName("Person"))
》初始化一个类的子类
》直接使用java.exe命令来运行一主类 程序先初始化主类