这几天学习jvm一些知识,刚好在牛客网上刷题时遇到一道关于jvm底层编译顺序的题目,看到问题也都是就java代码而言讲的,也没有剖析原理。我趁着所学jvm还热着,赶紧一下我的拙见,不然过不了多久估计我也不会了。 在牛客看到题目是这样的: 上面代码能否运行,如果能够运行,那最后输出的结果是什么? 这里你先思考一下,再继续阅读 这一串代码是可以运行的,结果为 5,和你思考的一样吗?其实这不单纯是 x=10; 然后x+=5;x值变成15,在最后x/=3;x的值变成5那么简单的哦。 讲类加载过程其实还需要了解class类怎么在jvm中的存储的。 首先大家要清楚一点就是jvm加载是懒加载(lazy loading)。懒加载通俗讲就是偷懒,用到谁加载谁,用不到就不加载。 类的生命周期是由7个阶段组成,但是类的加载说的是前5个阶段 这里补充一点:加载阶段主动使用子类父类也需要加载 我们看一下上面题目的字节码: 正如标题所说,我们直接来看代码 大家可以先看代码,然后自己去思考输出,然后再看我的解析会好点。 结果是: 附加 证明static String str = “A str”;其实它是存储在Test_1_A 的InstanceMirrorKlass中的。这个证明我们需要用到HSDB(不懂得百度一个神器) 结果是: 仔细审视,这和第二题是有区别的。区别在于final。 结果是:A Str 准备:为静态变量分配内存,赋初值(ps:实例对象new时直接赋值在准备阶段没有赋初值一说。如果被final修饰,在编译的时候会给属性添加ConstantValue属性,准备阶段直接完成赋值,即没有赋初值这一步) 上面也有这句话,你理解其中含义没?其实final修饰的,在编译过程中将str=A Str写入Test_6_A的常量池中,所以不需要调用Test_6_A,直接在常量池中就能找到(真是有够懒加载的对不···) 结果: 这个uuid虽说被finla修饰,但是右边是需要动态创建才能生成的,所以结果才会这样。 结果是:1 2 结果是:1 1 感谢大家的观看,有什么错误之处欢迎大家指出来。
目录
引子
public class Test { static int x=10; static {x+=5;} public static void main(String[] args) { System.out.println("x="+x); } static{x/=3;}; }
ps:防止大家不信我特意用idea编译运行一遍,把截图奉上以示自己正确性。
这里我先不分析原因,直接从jvm加载开始讲解,等到讲完大家也就知道原因了。准备阶段
如果说class是java的类(java代码)
那么InstanceKlass就是jave类在jvm中的存在形式(c++代码)classloader(类加载过程)
大致讲解5个阶段功能:
因为这一题,main方法肯定使用时在类加载过程的后面,所以到初始化的时候就会按顺序把static int x = 10; static {x+=5;} static{x/=3}执行。但其实这个题目比较简单,我们看几个复杂的例子,让大家彻底搞懂这块知识。Talk is cheap,show me your code.
Test_1
public class Test_1 { public static void main(String[] args) { System.out.printf(Test_1_B.str); while (true); } } class Test_1_A { public static String str = "A str"; static { System.out.println("A Static Block"); } } class Test_1_B extends Test_1_A { static { System.out.println("B Static Block"); } }
A Static Block
A str
差异吗?是不是很惊奇,待我慢慢讲来。
是否明白?而Test_1_B中是没有str的,所以main中调用时调用的其实是A的str,B用不到自然就不用加载了。
使用步骤就是:
先jps-l查看进程号
使用hsdb通过进程号查看进程的内存地址
通过内存地址搜索到存储的内容如下图(Test_1_A的jvm存储的信息,看到str被存在A中):
这里B就不演示搜索了,其实B中InstanceMirrorKlass是什么都没有的,也就能证明str这个静态String被存到A中。Test_2
public class Test_2 { public static void main(String[] args) { System.out.printf(Test_2_B.str); } } class Test_2_A { static { System.out.println("A Static Block"); } } class Test_2_B extends Test_2_A { public static String str = "B str"; static { System.out.println("B Static Block"); } }
A Static Block
B Static Block
B str
踩了第一个坑之后,第二个就好点了吧。这个如果做错就是上面有一句话没有注意:加载阶段主动使用子类父类也需要加载,那就是你调用子类,父类会间接调用。第一题告诉我们调用父类,却不会调用子类。Test_3
public class Test_3{ public static void main(String[] args) { System.out.println(Test_3_A.str); } } class Test_3_A { public static final String str = "A Str"; static { System.out.println("Test_6_A Static Block"); } }
这里牵扯到我前面介绍的另外一个点:
可以使用javap -vervose classpath 查询常量池
三个例子够不够?不够那再来,码字不易记得多多Test_4
public class Test_4 { public static void main(String[] args) { System.out.println(Test_4_A.uuid); } } class Test_4_A { public static final String uuid = UUID.randomUUID().toString(); static { System.out.println("Test_4_A Static Block"); } }
Test_4_A Static Block
38b41380-7111-4ce5-8079-5a2576fd4282Test_5
public class Test_5 { public static void main(String[] args) { Test_5_A obj = Test_5_A.getInstance(); System.out.println(Test_5_A.val1); System.out.println(Test_5_A.val2); } } class Test_5_A { public static int val1; public static int val2 = 1; public static Test_5_A instance = new Test_5_A(); Test_5_A() { val1++; val2++; } public static Test_5_A getInstance() { return instance; } }
这里我们一步一步分析,在前面的初始化阶段中:clinit方法中语句的先后顺序与代码的编写顺序相关,故val1=0;val2=1;
public static Test_5_A instance = new Test_5_A();这个会调用构造函数Test_5_A(),导致val1++,和val2++;
所以最后结果是1和2Test_6
public class Test_6 { public static void main(String[] args) { Test_6_A obj = Test_6_A.getInstance(); System.out.println(Test_6_A.val1); System.out.println(Test_6_A.val2); } } class Test_6_A { public static int val1; public static Test_6_A instance = new Test_6_A(); Test_6_A() { val1++; val2++; } public static int val2 = 1; public static Test_6_A getInstance() { return instance; } }
还是clinit方法中语句的先后顺序与代码的编写顺序相关,这次初始化顺序是val1=0;然后初始化public static Test_6_A instance = new Test_6_A();调用Test_6_A(),val1++,val2++;最后再次初始化val2=1;所以这里因为顺序关系导致覆盖。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算