1、一个类编译完后会生成一个class文件。Class文件经历以下过程进行执行:
1、加载:加载class文件的信息加载到内存中。由硬盘到内存的迁移。
将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象, 用来封装类在方法区内的数据结构。类加载的追中产品是位于堆区的class对象。
2、链接
类被加载后就进入了连接阶段,链接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行环境中去。
A)验证
验证被加载累的正确性
B)准备
为类的静态变量分配内存,并将其初始化为默认值
C)解析
把类中的符号引用转换为直接引用
如在work类中有一个方法:
Public void gotowork(){
Car.run();
}
在wokr类的二进制数据中,包含了一个对car类的run方法的符号引用,他有run()方法的全名和相关描述符组成。在解析阶段,java虚拟机会把这个符号引用直接替换成一个指针,该指针指向Car类的run()方法在方法区内的内存位置,这个指针就是直接引用。
3、初始化
为类的静态变量赋予正确的初始化值按照从上到小的顺序
假设类存在直接父类,并且这个父类还没有被初始化,那么就先初始化直接的父类,假设类中存在初始化语句,那么依次执行这些初始化语句。
在去直接引用一个类的静态变量的时候,如果这类变量定义为了final类型并且可以在编译就确定值的情况不会执行静态代码块,反之会执行。Eg:
Public Class Child{
Public static final x =6/3;
Static{
System.out.prinln(“dd”);//
}
}
如果在另外的一个类中去调用Child.x的时候不会执行静态代码块。
Public Class Child{
Public static final x =new Random.next(100);
Static{
System.out.prinln(“dd”);//
}
}
在别的类调用Child.x的时候会执行静态代码块
2、所有类或者接口被首次主动使用时候才会初始化这些类。Java对类的使用有两种:
主动使用
被动使用
主动使用:
1、创建类的实例
A a = new A(); 对A的主动使用
2、访问类的或者接口的静态变量,或者对该静态变量赋值
int b = Test.A 对Test类的主动使用
3、调用静态方法
Test.doSomthing()//对Test的主动使用
4、反射
反射api
5、初始化一个类的子类,看做对父类的主动使用
Class A{
}
Class B extends A{
}
B b = new B(); 对A也主动使用
6、Java虚拟机启动时被标注为启动类的类,即包含main方法。
注意:只有当程序访问的静态变量或静态方法确实在当前类或者当前接口中定定义才可人为是对类的或者接口的主动使用。如果一个静态变量 a 定义在父类中,如果子类Child.a调用的话子类不会被初始化
其余为被动使用,都不会导致类的初始化
3、类加载器(用于把类加载到内存的工具)
Java虚拟机自带的加载器
1、根类加载器(Bootstrap),c编写的
如果一个类使用根类加载器加载的话getClassLoader返回空
2、扩展类加载器(Extension)
3、系统加载器或者称为应用加载器(System)
用户自定义加载器
Java.lang.classLoader的子类
4、类加载器加载类时机
JVM规范允许加载器在预料某各类将要被使用的时候就预先加载他,如果在预先加载的过程中遇到了.class文件缺失或者存在错误,类加载器必须在程序首次主动使用该类时才会报告错误(LinkERROR错误),如果这个类一直没有被程序首次主动使用,那么类加载器不会报告该错误。