重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
前言
不了解JVM的类加载机制你也可以coding,但是当你了解之后,可以让你在coding的时候避免很多坑,本文将以一道常见的面试题去剖析一下。本文参考 深入理解Java虚拟机(第2版) 。
公司主营业务:网站设计制作、做网站、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联建站是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联建站推出铁山免费做网站回馈大家。
1 public class ClassLoadTest {
2 private static ClassLoadTest test = new ClassLoadTest();
3
4 static int x;
5 static int y = 0;
6
7 public ClassLoadTest() {
8 x++;
9 y++;
10 }
11
12 public static void main(String[] args) {
13 System.out.println(test.x);
14 System.out.println(test.y);
15 }
16}
这里大家可以先猜测一下答案,可能结果会出乎你的意料~
类加载过程
先用一个图简单的描述一下类加载的这个过程
加载
这个过程相当于从本地或者网络端去读取一个字节流,然后将一些静态储存结构转换成方法区中运行时期的数据,最后生成一个代表这个类的Class对象,作为方法区访问这个类的入口。
例如:
咱们可以通过一个类的全限定名去加载类
通过jar、war包去加载类
通过http请求去第三方平台上拉取指定的类来加载
针对上述例子,这里是加载一个 ClassLoadTest.class
对象。
验证
要理解这个环节并不是很难,一个东西要放到JVM上去运行,咱们肯定得对其进行一些过滤,不能啥都往上丢,这里的验证简单的举几个例子:
文件格式的验证:
①是否以魔数0xCAFEBABE开头;
②主次版本号是否在当前虚拟机处理范围内;
③常量池中的常量是否有不被支持的常量类型等等。
元数据的验证:
①这个类是否有父类;
②这个类的父类是否继承了不被允许继承的类(final修饰的类);
③这个类不是抽象类,是否实现了所有接口中要实现的方法等等。
字节码的验证:
①保证跳转指令不会跳转到方法体以外的字节码指令上;
②保证方法体中的类型转换是有效的等等。
准备过程
这个过程相当于给类变量分配内存并设置变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
针对上述例子:
1test = null;
2x = 0;
3y = 0;
注意:这里有个特殊情况,如果该字段被 final
修饰,那么在准备阶段改字段就会被设置成咱们自定义的值。 public static final int value = 11
,在准备阶段就会直接赋值11,并不是该变量的初始值。
解析过程
将符号引用转换成直接引用的过程。这里有两个名词 符号引用 和 直接引用 。
符号引用:符号引用与虚拟机的布局无关,甚至引用的目标不一定加载到了内存中。符号可以是任何形式的字面量,只要使用时能够准确的定位到目标即可。
而解析过程又会针对类、字段、方法进行解析,解析失败则会抛出相应的异常。例如在解析时发现没有访问权限会抛出 java.lang.IllegalAccessException
异常,查询不到引用字段会抛出 java.lang.NoSuchFieldException
异常,查询不到方法会抛出 java.lang.NoSuchMethodException
异常等等。
初始化
在准备阶段,变量已经赋值过系统要求的默认值,在初始化阶段,则会根据程序制定的主观计划去初始化类变量和其他资源。这句话听起来有些绕口,根据上述例子,实际上就是:
1test = new ClassLoadTest();// x = 1;y =1
2y = 0;
这个过程,由于 x
咱们自己并没有去设定一个值,所以初始化阶段它不会发生任何改变,但是 y
咱们有设定一个值0,所以最后造成最终结果为 x = 1;y = 0
。
ps:在同一个类加载器下,一个类只会初始化一次。多个线程同时初始化一个类,只有一个线程能正常初始化,其他线程都会进行阻塞等待,直到活动线程执行初始化方法完毕。
总结
对于上面这个面试题,咱们用流程图简单的描述一下:
对Java技术,架构技术感兴趣的同学,欢迎加群,一起学习,相互讨论。可以获取免费的学习资料,群号:614478470 点击加入