Java类的生命周期(面试题)
当我们在本地编写完java代码的时候,是.java文件,在编译过后会成为.class字节码文件,.class字节码文件只能在虚拟机中运行,Java中类的声明周期就是.class文件从加载到销毁的过程。
Java类的生命周期主要有加载、连接、初始化、使用、卸载这五个阶段。如图:
加载:
在这个阶段,JVM会找到类并把其信息加载到方法区中,然后在堆内存中实例化一个java.lang.Class对象,作为这个方法区中这个类的入口。
加载方式如下:
- 根据类的全路径名加载.class文件
- 根据规则实时生成,比如动态代理
加载时机:并不是启动JVM的时候就会加载这个类,而是在使用这个类的时候才会去加载。
连接:
连接和加载是交替进行的,连接阶段主要做类加载完成后和初始化前的准备工作,主要有三部分:验证、准备、解析
- 验证: 对字节码信息进行语义分析,保证其符合Java语法要求,比如说检查时候继承了final类等。
- 准备: 为类变量(static变量)分配内存,设置初始值,public static int value = 2; 初始化后value = 0,如果是boolean值,默认为false,如果是变量应用,默认为null;但是如果是final:public static final int value = 123; 在初始化后就会赋正确的值123
- 解析: 把常量池中的符号引用转化为直接引用
初始化:
初始化静态变量和静态代码块,谁先谁执行。以下程序中,最后的age是多少,输出的就是多少。
/**
* @author : Lxd
* @date : 18:47 2021/5/27
*/
public class Test {
//public static int age = 60;
static{
age = 100;
}
public static int age = 60;
public static void main(String[] args) {
System.out.println(Test.age);
}
}
初始化的时机:直接引用一个类会触发类的初始化
- 通过new 新建一个对象的时候,使用类的静态变量的时候;
- 作为程序入口直接调用的时候,即main方法在这个类里边;
- 初始化子类的时候,父类也会初始化,如下:
/**
* @author : Lxd
* @date : 18:47 2021/5/27
*/
public class Test {
static{
System.out.println("父类初始化");
}
}
class test1 extends Test{
static {
System.out.println("子类初始化");
}
}
class Main{
public static void main(String[] args) {
test1 t = new test1();
}
}
//输出结果:
/*
父类初始化
子类初始化
*/
使用:
- 引用父类的静态变量只会引起父类的初始化;
- 引用常量不会初始化;
- 创建对象数组不会初始化;
/**
* @author : Lxd
* @date : 18:47 2021/5/27
*/
public class Test {
static String abc = null;
static final String str = "a";
static{
System.out.println("父类初始化");
}
}
class test1 extends Test{
static {
System.out.println("子类初始化");
}
}
class Main{
public static void main(String[] args) {
System.out.println(Test.abc); //引用父类的静态变量,只初始化父类
//System.out.println(Test.str); //引用父类的常量,不会初始化
Test[] tests = new Test[12]; //创建对象数组不会初始化
}
}
卸载:
在类使用完后,满足以下任三个条件,JVM就会回收卸载
- 该类的所有实例都被回收,也就是说在堆中不存在该类的任何实例;
- 该类的ClassLoader已经被回收;
- 该类的Class对象没有被任何一个地方直接或者间接引用;
满足这三个条件,方法区进行垃圾回收的时候就会回收卸载该类。
总结:
Java对象的生命周期只是类生命周期的一部分,即主动引用部分,类的声生命周期要比对象的生命周期长的多。