概述

虚拟机把类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。
java类的加载,连接和初始化都是在程序运行时完成的,这种策略增加了一定开销,但是提高了灵活性,如:
1.对于面向接口的编程,可以根据实际情况决定实现类
2.用户可以自定义类加载器,让本地的应用程序通过网络或者其他地方加载一个二进制流做为文件。
类加载时机

五总初始化场景

1.遇到new,getstatsic,putstatic或者invokestatic这4条字节码指令时
2.使用java.lang.reflect包的方法对类进行反射调用时
3.一个类如果父类还没有初始化,初始化父类
4.虚拟机启动使,先初始化用户的主类
5.使用JDK动态语言支持时

被动引用

上述五总场景的行为称为对一个类进行主动引用,除此之外,所有引用类的方法都不会触发初始化,称为被动引用。
1.当通过之类去调用父类的静态方法时,只会初始化父类
2.当创建一个对象数组时,不会初始化该类,此时初始化的是另外一个[L{原类地址}]的类,该类由虚拟机自动生成,直接继承与java.lang.object的子类,创建动作由字节码指令newarrary生成
3.调用static final 常量时,不会初始化类,因为在编译时会保存到常量池中
4.接口在初始化时,只有在真正使用其父类时才会初始化其父类

类加载过程

加载

在加载为类加载过程的一个阶段, 此阶段虚拟机需要完成下面3件事情:
1.通过一个类的权限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态结构转化为方法区的运行数据
3.在内存中生成一个代表这个类的java.lang.class对象,做为方法区这个类的各种数据的入口。
请中“通过一个类的权限定名来获取定义此类的二进制字节流。”中二进制字节流可以从很多地方获取如:
1.从jar,ear,war包获取
2.从网络获取
3.动态代理
4.其他文件生成(如jsp)等

验证

验证是连接阶段的第一步,这一阶段的目的是为了确保class文件的字节流中包含的信息符合当前虚拟接的要求。
1.文件格式验证
2.元数据验证:是否有父类,是否继承了不允许继承的final类,如果不是抽象类,是否复写了父类的方法等
3.字节码验证:保证类型转换有效等
4.符号引用验证:通过字符串描述的全限定名是否可以找到对应类,在可以找到雷总指定的对象,符号引用中的类的字段和方法是否可以被当前类引用到

准备

为类的变量加载初始值的阶段。如:static int value = 123 的初始值是0,因为123实在初始化的时候才加载。但是如果staticfinal int value = 123 因为是常量,在准备阶段就加载了。

解析

将符号引用(class或其他文件中描述类的信息)替换为直接引用(指针,内存地址)。

初始化

初始化阶段是执行类的构造器()方法的过程
1.()方法是由编译器自动收集类中的所有变量的赋值动作和静态语块产生的
在静态语句前的变量只能被赋值不能被访问。
2.虚拟机保证子类的方法执行之前,父类的()已经执行
3.()如果类中没有静态语句块也没有变量赋值操作,编译器可以不为这个类生成方法。
4.其他clinti说明参考“被动引用”

类加载器

同一个类用不同的类加载器加载,会被认为是两个独立的类。
类加载器可以分为:
1.启动类加载器java.lang.class,由虚拟机实现,负责加载<JAVA_HOME>\lib目录中的class
2.扩展类加载器,由sun.misc.LanuncherKaTeX parse error: Undefined control sequence: \lib at position 34: …负责加载<JAVA_HOME>\̲l̲i̲b̲\ext下的class 3.应…AppClassLoader实现。负责加载用户路径ClassPath上的class
4.自定义的加载器
这些类加载器都继承与java.lang.class.
一般类加载器都符合双亲委派模型:即父类可以加载的类由父类完成,不能由父类完成的再由子类加载器完成。但是在很多情况下也可以跳出这个规则,如JNDI服务等。