1.类的加载过程
一个java文件从被加载到被卸载这个生命过程,总共要经历5个阶段,JVM将类加载过程分为: (加链初使卸)
加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载
(1)加载
首先通过一个类的全限定名来获取此类的二进制字节流。再将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。最后在java堆中生成一个代表这个类的class对象。作为方法区中这些数据的访问入口。即查找并加载类的字节码文件(*.class)(二进制数据)。
(2)链接
验证:确保被加载类的正确性。
准备:为类的静态变量分配内存,并将其初始化为默认值。
解析:把类中的符号引用转换为直接引用。
(3)类的初始化
> 1.类什么时候初始化?
1)创建类的实例,即new一个对象时
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)初始化一个类的子类(先初始化类的父类)
> 2.类的初始化顺序
1)如果这个类还没有被加载和链接,那先进行加载和链接
2)如果当前类存在直接父类,并且这个类还没有被初始化(注:一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
3)加入类中存在初始化语句(static变量和static代码块),依次执行这些初始化语句
初始化顺序依次是:静态变量、静态初始化块–>变量、初始化块–>构造器
(有父类)顺序为:父类static方法–>子类static方法–>父类构造方法–>子类构造方法
例:
class A{
static{
System.out.println("这是A的静态代码块!");
}
}
class B extends A{
static{
System.out.println("这是B的静态代码块!");
}
}
public class ClassLoadOrder {
public static void main(String[] args) {
System.out.println("start!");
new B();
System.out.println("end!");
}
}
运行结果:
class AA{
int a = initA();
private int initA(){
System.out.println("AA初始化a");
return 0;
}
static{
System.out.println("aaaa");
}
{
System.out.println("AA的构造代码块");
}
AA(int x){
System.out.println("AA的有参构造方法");
}
}
class BB extends AA{
int b = initB();
private int initB(){
System.out.println("BB初始化b");
return 0;
}
static{
System.out.println("bbbb");
}
{
System.out.println("BB的构造代码块");
}
BB(int x){
super(x);
System.out.println("BB的有参构造方法");
}
BB(){
this(100);
System.out.println("BB的无参构造方法");
}
}
public class InstanceOrder {
//类加载的顺序
//1.静态代码块
//2.初始化代码块
//3.构造代码块
//4.构造方法代码块
public static void main(String[] args) {
new BB();
}
}
运行结果:
class HelloA {
//构造⽅法
public HelloA(){
System.out.println("Hello A!⽗类构造⽅法");
}
//⾮静态代码块
{
System.out.println("i'm A class.⽗类⾮静态代码块");
}
//静态代码块
static{
System.out.println("static A ⽗类静态代码块");
} }
public class HelloB extends HelloA {
//构造⽅法
public HelloB(){
System.out.println("Hello B! 构造⽅法");
}
//⾮静态代码块
{
System.out.println("i'm B class.⾮静态代码块");
}
//静态代码块
static{
System.out.println("static B 静态代码块");
}
public static void main(String[] args) {
System.out.println("---start---");
new HelloB();
new HelloB();
System.out.println("---end---");
}
}
运行结果:
总结:
1)所有的类都会优先加载父类
2)优先初始化静态成员
3)成员初始化后,才会执行构造方法
4)静态成员的初始化和静态块的执行,发生在类加载的时候
5)类对象的创建以及静态块的访问,都会触发类的加载