TOC
Java虚拟机是如何加载Java类的?
虚拟机规范并没有指明二进制字节流要从一个Class文件获取,或者说根本没有指明从哪里获取、怎样获取。这种开放使得Java在很多领域得到充分运用,例如:
- 从ZIP包中读取,这很常见,成为JAR,EAR,WAR格式的基础
- 从网络中获取,最典型的应用就是Applet
- 运行时计算生成,最典型的是动态代理技术,在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass来- 为特定接口生成形式为“*$Proxy”的代理类的二进制字节流
- 由其他文件生成,最典型的JSP应用,由JSP文件生成对应的Class类
从class文件到内存中的类,按先后顺序需要经过加载、链接、以及初始化三大步骤。
其中,链接过程中同样需要验证;而内存中的类没有经过初始化,同样不能使用。 基本类型和引用类型 - 基本类型,是由Java虚拟机预先定义好的。 - 另一大类引用类型,Java将其分为四种:类、接口、数组类和泛型参数(泛型参数会在编译过程中被擦除),因此Java虚拟机实际上只有前三种。
在类、接口和数组类中,数组类是由Java虚拟机直接生成的,其他两种则由其对应的字节流。
字节流,最常见的形式要属由Java编译器生成的class文件
步骤一:加载
是指查找字节流,并且据此创建类的过程。
类加载器,双亲委派模型
启动类加载器(boot class loader):c++实现的
其他类都是java.lang.ClassLoader
的子类,都需要先由启动类加载器,加载至Java虚拟机中,方能执行类加载。
扩展类加载器(父类是启动类加载器):加载相对次要、但又通用的类,比如存放在JRE的lib/ext目录下jar包中的类(以及由系统变量java.ext.dirs指定的类)
应用类加载器的父类加载器则是扩展类加载器。它负责加载应用程序路径下的类。
双亲委派模型:每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载
双亲委派模式优势
采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
步骤二:链接
是指将创建的类合并至Java虚拟机中,使之能够执行的过程。
1.验证阶段
确保加载类能够满足Java虚拟机的约束条件
2.准备阶段
为被加载类的静态字段分配内存,类变量赋默认值
3.解析阶段
非必须(动态绑定)