1.类连接
1.1 类连接主要验证的内容
- 类文件结构检查:按照JVM规范规定的类文件结构进行
- 元数据验证:对字节码描述的信息进行语义分析,保证其符合Java语言规范要求
- 字节码验证:通过对数据流和控制流进行分析,确保程序语义是合法和符合逻辑的。这里主要对方法进行校验
- 符号引用验证:对类自身以外的信息,也就是常量池中的各种符号引用,进行匹配校验
1.2 类连接中的解析
所谓解析就是把常量池中的符号引用转换成直接引用的过程,包括:符号引用:以一组无歧义的符号来描述所引用的目标,与虚拟机的实现无关
- 直接引用:直接指向目标的指针、相对偏移量、或是能间接定位到目标的句柄,是和虚拟机实现相关的。
- 主要针对:类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符
2.类的初始化
类的初始化就是为类的静态变量赋初始值,或者说是执行类构造器<clinit>方法的过程
- 如果类还没有加载和连接,就先加载和连接
- 如果类存在父类,且父类没有初始化,就先初始化父类
public class MyParent {
static {
System.out.println("my parent class init");
}
}
public class MyChild extends MyParent {
static {
System.out.println("my child class init");
}
}
public class Test {
public static void main(String[] args) {
MyChild myChild = new MyChild();
}
}
my parent class init
my child class init
- 如果类中存在初始化语句,就依次执行这些初始化语句
public class MyChild extends MyParent {
static {
System.out.println("my child class init");
}
static {
System.out.println("my child class block 1");
}
private static final int a = 5;
static {
System.out.println("my child class block 2 a="+a);
}
}
my parent class init
my child class init
my child class block 1
my child class block 2 a=5
- 如果是接口的话:
- 初始化一个类的时候,并不会先初始化它实现的接口
- 初始化一个接口时,并不会初始化它的父接口
- 只有当程序首次使用接口里面的变量或者是调用接口方法的时候,才会导致接口初始化
public interface Api {
public static String str = "now in api";
public void t1();
}
public class MyChild extends MyParent implements Api{
static {
System.out.println("my child class init");
}
static {
System.out.println("my child class block 1");
}
private static final int a = 5;
static {
System.out.println("my child class block 2 a="+a);
}
@Override
public void t1() {
System.out.println("now in mychild t1");
}
}
public class Test1 {
public static void main(String[] args) {
MyChild myChild = new MyChild();
System.out.println("mychild.str=="+myChild.str);
}
}
my parent class init
my child class init
my child class block 1
my child class block 2 a=5
mychild.str==now in api
ZHI
5.调用Classloader类的loadClass方法来转载一个类,并不会初始化这个类,不是对类的主动调用
public static void main(String[] args) throws ClassNotFoundException {
MyClassLoader myClassLoader = new MyClassLoader("myClassloader1");
Class cls1 = myClassLoader.loadClass("com.kpioneer.demo.jvm.classinit.MyChild");
System.out.println("over=======");
}
over=======
Classloader调用MyChild,里面的静态代码块,并没有被调用。
2.1.类的初始化时机
Java程序对类的使用方式分成:主动使用和被动使用,JVM必须在每个类或接口"首次主动使用"时才初始化它们;被动使用类不会导致类的初始化,主动使用的情况:
1)创建类实例
2)访问某个类或接口的静态变量
3)调用类的静态方法
public class MyChild extends MyParent implements Api {
private static final int a = 5;
static {
System.out.println("my child class init");
}
static {
System.out.println("my child class block 1");
}
static {
System.out.println("my child class block 2 a=" + a);
}
public static void t2() {
System.out.println("now in mychild t2");
}
@Override
public void t1() {
System.out.println("now in mychild t1");
}
}
public static void main(String[] args) throws ClassNotFoundException {
MyChild.t2();
}
my parent class init
my child class init
my child class block 1
my child class block 2 a=5
now in mychild t2
4)反射某个类
public static void main(String[] args) throws ClassNotFoundException {
Class cls = Class.forName("com.kpioneer.demo.jvm.classinit.MyChild");
}
my parent class init
my child class init
my child class block 1
my child class block 2 a=5
5)初始化某个类的子类,而父类还没有初始化
6)JVM启动的时候运行的主类
public class Test {
static {
System.out.println("now Test class init");
}
public static void main(String[] args) throws ClassNotFoundException {
Class cls = Class.forName("com.kpioneer.demo.jvm.classinit.MyChild");
}
}
now Test class init
my parent class init
my child class init
my child class block 1
my child class block 2 a=5
7)定义了default方法的接口,当接口实现类初始化时
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Api api = (Api)(Class.forName("com.kpioneer.demo.jvm.classinit.MyChild").newInstance());
api.t3();
}
my parent class init
my child class init
my child class block 1
my child class block 2 a=5
now in api t3()
3.类的卸载
- 当代表一个类的Class对象不在引用,那么Class对象的生命周期就结束了,对应的在方法区中的数据也会被卸载
- JVM自带的类加载器装载的类,是不是会卸载的,有用户自定义的类加载器加载的类是可以卸载的
网友评论