JAVA

面试官:java类的加载过程

2019-09-16  本文已影响0人  秦时的明月夜

Java 类加载机制

类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。

类的加载过程.png

类的加载过程

加载

1、通过类加载器,加载.class文件到内存中。
2、将读取到.classs数据存储到运行时内存区的方法区。
3、然后将其转换为一个与目标类型对应的java.lang.Class对象实例。这个Class对象在日后就会作为方法区中该类的各种数据的访问入口。

链接

验证

确保被加载的类(.class文件的字节流),是否按照java虚拟的规范。不会造成安全问题
1、文件格式验证:
第一阶段要验证字节流是否符合 Class文件格式的规范, 井且能被当前版本的虚拟机处理。这一阶段可能包括下面这些验证点:

2、元数据验证
第二阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求,这个阶段可能包括的验证点如下:

3、字节码验证
第三阶段是整个验证过程中最复杂的一个阶段, 主要目的是通过数据流和控制流的分析,确定语义是合法的。符号逻辑的。在第二阶段对元数据信息中的数据类型做完校验后,这阶段将对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为,例如:

4、符号引用验证
最后一个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候 , 这个转化动作将在连接的第三个阶段——解析阶段中发生。符号引用验证可以看做是对类自身以外(常量池中的各种符号引用) 的信息进行匹配性的校验, 通常需要校验以下内容:

符号引用中通过字将串描述的全限定名是否能找到对应的类
在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段 。
符号引用中的类、字段和方法的访问性(privateprotectedpublicdefault)是否可被当前类访问
符号引用验证的目的是确保解析动作能正常执行, 如果无法通过符号引用验证, 将会抛出一个java.lang.IncompatibleClassChangError异常的子类, 如 java.lang.IllegalAccessErrorjava.lang.NoSuchFieldErrorjava.lang.NoSuchMethodError等。

准备

主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值,此时的赋值是Java虚拟机根据不同变量类型的默认初始值:
8种基本类型的初值,默认为0;引用类型的初值则为null;常量的初值即为代码中设置的值
1、final static temp = 100,此时temp就是赋值 100
2、String temp = “123456”,此时temp值就是null
3、int temp = 100,此时temp值就是0

解析

将类的二进制数据中的符号引用替换成直接引用(符号引用是用一组符号描述所引用的目标;直接引用是指向目标的指针)

可以认为是一些静态绑定的会被解析,动态绑定则只会在运行时进行解析;静态绑定包括一些final方法(不可以重写),static方法(只会属于当前类),构造器(不会被重写)

在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。

初始化

初始化,则是为标记为常量值的字段赋值的过程。
换句话说,只对static修饰的变量或语句块进行初始化。
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

涉及问题

一个类的构造器,代码块,静态代码块,成员变量的 的执行顺序。

//父类
public class ParentClass {
    private int p1 = getValue() ;
    private static int p2 = getValue2();
    public ParentClass(){
        System.out.println("我是父构造器");
    }
    static {
        System.out.println("我是父静态代码块1");
    }
    static {
        System.out.println("我是父静态代码块2");
    }
    {
        System.out.println("我是父代码块1");
    }
    {
        System.out.println("我是父代码块2");
    }

    private int getValue(){
        System.out.println("我是父成员变量p1");
        return 1;
    }

    private static int getValue2(){
        System.out.println("我是父静态成员变量p2");
        return 1;
    }

}
//子类
public class ChildClass extends ParentClass{
    private int c1 = getValue() ;
    private static int c2 = getValue2();

    public ChildClass(){
        System.out.println("我是子构造器");
    }
    static {
        System.out.println("我是子静态代码块1");
    }
    static {
        System.out.println("我是子静态代码块2");
    }
    {
        System.out.println("我是子代码块1");
    }
    {
        System.out.println("我是子代码块2");
    }

    private int getValue(){
        System.out.println("我是子成员变量c1");
        return 1;
    }

    private static int getValue2(){
        System.out.println("我是子静态成员变量c2");
        return 1;
    }

    public static void main(String[] args) {
        ChildClass childClass = new ChildClass();
    }
}

执行结果:
我是父静态成员变量p2
我是父静态代码块1
我是父静态代码块2
我是子静态成员变量c2
我是子静态代码块1
我是子静态代码块2
我是父成员变量p1
我是父代码块1
我是父代码块2
我是父构造器
我是子成员变量c1
我是子代码块1
我是子代码块2
我是子构造器

上一篇 下一篇

猜你喜欢

热点阅读