java类加载过程

今天早上的时候被问到了类的加载机制,emm,然后我就不确定的说是不是 那5个阶段:

加载 验证 准备 解析 (前面的这三步也可以被说成链接) 初始化 幸好被我蒙混过关了,然后就开始在网上查找资料,来完善下:

​ https://www.cnblogs.com/aspirant/p/7200523.html

​ https://www.jb51.net/article/92758.htm

​ https://zhuanlan.zhihu.com/p/66984952

​ 这些都是参考的文章

首先java源码编译由3个过程组成:

​ 1.源码编译机制

​ 2.类加载机制

​ 3.类执行机制

1568254348465

编译

​ 利用javac可以将.java文件编译成java文件----其实这里有个小细节,那就是java是编译型语言还是解释型语言。

然后我查了下知乎发现也是模棱两可的,说所有java文件都需要编译才能执行,因此时编译型,但是java代码是解释运行在jvm上的,然后jvm从.class文件中读取一行解释一行,因此时解释型---------》这里还可以再深入https://www.jianshu.com/p/79046f74fde8

类加载

我发现有人的关于加载的写的特别好,我就直接复制这一段把 https://zhuanlan.zhihu.com/p/25228545##1

1、验证:确保被加载的类的正确性

验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:

文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。

元数据验证:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。

字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。

符号引用验证:确保解析动作能正确执行。

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

2、准备:为类的静态变量分配内存,并将其初始化为默认值

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:

1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。

2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。

假设一个类变量的定义为:public static int value = 3; 那么变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器()方法之中的,所以把value赋值为3的动作将在初始化阶段才会执行。

3、解析:把类中的符号引用转换为直接引用

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

3) 初始化:对类的静态变量,静态代码块执行初始化操作初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

①声明类变量是指定初始值。

②使用静态代码块为类变量指定初始值。

补充:关于初始化:https://www.jb51.net/article/92758.htm

初始化阶段

初始化是类加载机制的最后一步,这个时候才正真开始执行类中定义的JAVA程序代码。在前面准备阶段,类变量已经赋过一次系统要求的初始值,在初始化阶段最重要的事情就是对类变量进行初始化,关注的重点是父子类之间各类资源初始化的顺序。

java类中对类变量指定初始值有两种方式:1、声明类变量时指定初始值;2、使用静态初始化块为类变量指定初始值。

初始化的时机

1)创建类实例的时候,分别有:1、使用new关键字创建实例;2、通过反射创建实例;3、通过反序列化方式创建实例。

2)调用某个类的类方法(静态方法)

3)访问某个类或接口的类变量,或为该类变量赋值。

4)初始化某个类的子类。当初始化子类的时候,该子类的所有父类都会被初始化。

5)直接使用java.exe命令来运行某个主类。

除了上面几种方式会自动初始化一个类,其他访问类的方式都称不会触发类的初始化,称为被动引用。

1、子类引用父类的静态变量,不会导致子类初始化。

2、通过数组定义引用类,不会触发此类的初始化

3、引用常量时,不会触发该类的初始化

用final修饰某个类变量时,它的值在编译时就已经确定好放入常量池了,所以在访问该类变量时,等于直接从常量池中获取,并没有初始化该类。

初始化的步骤

1、如果该类还没有加载和连接,则程序先加载该类并连接。

2、如果该类的直接父类没有加载,则先初始化其直接父类。

3、如果类中有初始化语句,则系统依次执行这些初始化语句。

在第二个步骤中,如果直接父类又有直接父类,则系统会再次重复这三个步骤来初始化这个父类,依次类推,JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及所有的父类都会被初始化。

下面的这个是我在看张龙的深入理解JVM里发现的
B站链接https://www.bilibili.com/video/av47756459?from=search&seid=1762487543676291633
我原先就以为第一个会报错,因为那个value2明明还没有定义,其实这个就说明了 static修饰的就是类加载就有了的

public class CeShi {
    static {
        value2 = 66;
    }

    public static int value1 = 5;
    public static int value2 = 6;


    public static void main(String[] args) {
        System.out.println(value2);//6
    }
}
public class CeShi {


    public static int value1 = 5;
    public static int value2 = 6;

    static {
        value2 = 66;
    }
    public static void main(String[] args) {
        System.out.println(value2);//66
    }
}
int value2 = 6;

    static {
        value2 = 66;
    }
    public static void main(String[] args) {
        System.out.println(value2);//66
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值