编译:即javac的过程,即把.java文件编译成.class文件,即编译成字节码文件,同时作一些类型以及格式的检查。java
类只有在要运行的时候才会被加载进JVM,即编译后只有须要到这个类的时候才会把他加载进JVM运行,这种动态加载是依靠反射来实现的,通常来讲一个class只会被加载一次,下一次就会从jvm的class缓存中获取,不会再去文件系统中去获取class文件了。c++
下面具体说说类的加载过程:程序员
类的装载方式分两种:数组
1.隐式装载:即平时咱们经过new产生对象时,他隐式的调用类装载器把类就加载进jvm中。没法进行动态加载,即你new的这个 class对象必须是你程序代码编译的时候有的。缓存
2.显式装载:即利用Class.forname()来显示的加载一个类。便可以实现动态加载,你能够在程序运行时加载进一个编译时并无的 类。安全
类的装载器(负责把类加载进jvm)jvm
有三种类装载器,为何要有三个类加载器,一方面是分工,各自负责各自的区块,另外一方面为了实现委托模型,那么遇到要加 载某一个类,三个类装载器之间是如何工做的,具体见另外一篇《深刻理解java类加载器ClassLoader》。url
类装载器是如何工做的,其实咱们能够本身实现一个类加载器:.net
try {
URL url = new URL( "file:/d:/test/lib/" ); //根据路径
URLClassLoader urlCL = new URLClassLoader( new URL[]{url});
Class c = urlCL.loadClass("TestClassA" );
TestClassA object = (TestClassA)c.newInstance();
object.method();
}catch (Exception e){
e.printStackTrace();
} 对象
从上面的代码应该能基本知道类加载器的实现。
接下来具体讲讲类加载的整个过程:
当咱们想执行一个.class文件的时候,java.exe会帮助咱们找到JRE,接着找到位于JRE中的jvm.dll,这就是java虚拟机,虚拟机激活后,会先作一些初始化的动做,一旦初始化动做完成后,就会产生第一个类加载器--Bootstrap Loader(它是由c++编写的),而后Bootstrap Loader加载Launcher.java中的ExtClassLoader加载器,并设定其parent为null(由于其parent是Bootstrap Loader,它是由c++编写的,没法找到这个实例),而后Bootstrap Loader再要求加载Launcher.java中的AppClassLoader加载器,并设定其parent为
ExtClassLoader。因此ExtClassLoader加载器和AppClassLoader加载器都是由Bootstrap Loader加载的,parent与是谁加载的并无关系。
有了类加载器以后,那么这三个加载器之间如何协同工做,能够看《深刻理解java类加载器ClassLoader》。至因而如何把.class文件加载进jvm的过程,从上面自定义一个类装载器也能够看出。
这样加载过程就差很少了,可是一个类要可使用还要进行连接,初始化两个过程。
加载-->连接-->初始化 其中连接还包括验证-->准备--》解析三个过程
一、加载(即上面说的整个过程)
类的加载阶段,主要是获取定义此类的二进制字节流,并将这个字节流所表明的静态存储结构转化为方法区的运行时数据结 构,最后在Java堆中生成一个表明这个类的java.lang.Class对象做为方法区这些数据的访问入口。相对于类加载过程的其余 阶段,加载阶段是开发期可控性最强的阶段。咱们能够经过定制不通的类加载器,也就是ClassLoader来控制二进制字节流的 获取方式。
二、验证
验证,准备和解析其实都属于链接阶段,而验证就是链接阶段的第一步。这一阶段主要是为了确保Class文件的字节流中包含 的信息复合当前虚拟机的要求,而且不会危害虚拟机自身的安全。主要验证过程包括:文件格式验证,元数据验证,字节码验 证以及符号引用验证。
三、准备
准备阶段正式为类变量分配内存并设置初始值。这里的初始值并非初始化的值,而是数据类型的默认零值。这里提到的类 变量是被static修饰的变量,而不是实例变量。关于准备阶段为类变量设置零值的惟一例外就是当这个类变量同时也被final 修饰,那么在编译时,就会直接为这个常量赋上目标值。
四、解析
解析时虚拟机将常量池中的符号引用替换为直接引用的过程。
五、初始化
在准备阶段,变量已经赋过一次系统要求的初始值,在初始化阶段,则是根据程序员经过程序的主观计划区初始化类变量和其 他资源。Java虚拟机规范规定了有4种状况必须当即对类进行初始化(加载,验证,准备必须在此以前完成)
1)当使用new关键字实例化对象时,当读取或者设置一个类的静态字段(被final修饰的除外)时,以及当调用一个类的静态 方法时(好比构造方法就是静态方法),若是类未初始化,则需先初始化。
2)经过反射机制对类进行调用时,若是类未初始化,则需先初始化。
3)当初始化一个类时,若是其父类未初始化,先初始化父类。
4)用户指定的执行主类(含main方法的那个类)在虚拟机启动时会先被初始化。
除了上面这4种方式,全部引用类的方式都不会触发初始化,称为被动引用。如:经过子类引用父类的静态字段,不会致使子类 初始化;经过数组定义来引用类,不会触发此类的初始化;引用类的静态常量不会触发定义常量的类的初始化,由于常量在编 译阶段已经被放到常量池中了。