这篇文章给大家介绍如何解析Java虚拟机的Class文件,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
前面发了几篇学习笔记,但是看这些东西总是感觉很"玄乎",来一篇实战的东西来揭一下"JVM"的面纱,让"SSH"时代的童鞋们来熟悉一下Java的"老祖爷"JVM。由于自己的水平有限,所以大家在看过程中发了什么问题,或者您有什么疑问请及时提出来,我及时解决。如果您有什么建议,那么更好大家一块讨论。
1、源码文件
public class LearningClassFile { //普通变量 private int id1; //静态变量 private static int id2; //常量 private final int id3 = 4; //静态常量 private static final int id4 = 5; public LearningClassFile() { } public LearningClassFile(int id1, int id2) { this.id1 = id1; this.id2 = id2; } //使用public修饰的addPub方法 public void addPub(int a, int b) { int result = a + b; System.out.println(result); } //使用private修饰的addPri方法 private void addPri(int a, int b) { int result = a + b; System.out.println(result); } //使用static修饰的方法 public static void addSta() { int result = id2 + id4; System.out.println(result); } public static final void addFinal(int a, int b) { int result = a + b; System.out.println(result); } public static void main(String[] args) { LearningClassFile lcf = new LearningClassFile(1, 2); lcf.addPub(1, 2); lcf.addPri(1, 2); addSta(); addFinal(1, 2); } }
Class文件:
Compiled from "LearningClassFile.java" public class LearningClassFile extends java.lang.Object SourceFile: "LearningClassFile.java" minor version: 0 major version: 50 //运行时常量池:用于存放编译期生成的各种字面量和符号引用。 Constant pool: //从父类Object继承的默认构造方法 //观察该方法的特征:无参,返回类型void const #1 = Method #13.#35; // java/lang/Object."<init>":()V //常量id3 //"#7.#36; // LearningClassFile.id3:I" //#7:查找常量池中的类名LearningClassFile //#36-->"const #36 = NameAndType #17:#15;// id3:I" //NameAndType字面的意思是名称和类型。即id3是变量的名称,I表示id3是int类型 //综合描述:LearningClassFile中的id3是int类型 const #2 = Field #7.#36; // LearningClassFile.id3:I const #3 = Field #7.#37; // LearningClassFile.id1:I const #4 = Field #7.#38; // LearningClassFile.id2:I //将System的out存储至常量池 //System类中out被public static final修饰的 //"public final static PrintStream out = nullPrintStream();" //综合描述:System类的out属性是PrintStream类型 const #5 = Field #39.#40; // java/lang/System.out:Ljava/io/PrintS tream; //将PrintStream的Println()方法存储至常量池 //该方法的参数为I,返回值为void const #6 = Method #41.#42; // java/io/PrintStream.println:(I)V //类LearningClassFIle const #7 = class #43; // LearningClassFile //构造函数 //该构造函数需传入两个int类型的变量 const #8 = Method #7.#44; // LearningClassFile."<init>":(II)V //LearningClassFile的addPub方法 //#4-->"const #45 = NameAndType #27:#26;// addPub:(II)V" //#27-->"const #27 = Asciz addPub;" 方法的名称为:addPub //#26-->"const #26 = Asciz (II)V;" 方法的类型:两个int类型的参数,返回类型为void const #9 = Method #7.#45; // LearningClassFile.addPub:(II)V const #10 = Method #7.#46; // LearningClassFile.addPri:(II)V const #11 = Method #7.#47; // LearningClassFile.addSta:()V const #12 = Method #7.#48; // LearningClassFile.addFinal:(II)V const #13 = class #49; // java/lang/Object const #14 = Asciz id1; const #15 = Asciz I; const #16 = Asciz id2; const #17 = Asciz id3; //ConstantValue属性表示一个常量字段的值 //即final修饰的属性 const #18 = Asciz ConstantValue; //对于final修饰的常量直接将类型和值存入常量池 const #19 = int 4; const #20 = Asciz id4; const #21 = int 5; const #22 = Asciz <init>; const #23 = Asciz ()V; //Code属性只为***一个方法、实例类初始化方法或类初始化方法保存Java虚拟机指令及相关辅助信息 //简而言之:保存方法编译后的指令信息 const #24 = Asciz Code; //java源码行号与编译后的字节码指令的对应表 const #25 = Asciz LineNumberTable; const #26 = Asciz (II)V; const #27 = Asciz addPub; const #28 = Asciz addPri; const #29 = Asciz addSta; const #30 = Asciz addFinal; const #31 = Asciz main; const #32 = Asciz ([Ljava/lang/String;)V; //java 源码文件 const #33 = Asciz SourceFile; const #34 = Asciz LearningClassFile.java; const #35 = NameAndType #22:#23;// "<init>":()V const #36 = NameAndType #17:#15;// id3:I const #37 = NameAndType #14:#15;// id1:I const #38 = NameAndType #16:#15;// id2:I const #39 = class #50; // java/lang/System const #40 = NameAndType #51:#52;// out:Ljava/io/PrintStream; const #41 = class #53; // java/io/PrintStream const #42 = NameAndType #54:#55;// println:(I)V const #43 = Asciz LearningClassFile; const #44 = NameAndType #22:#26;// "<init>":(II)V const #45 = NameAndType #27:#26;// addPub:(II)V const #46 = NameAndType #28:#26;// addPri:(II)V const #47 = NameAndType #29:#23;// addSta:()V const #48 = NameAndType #30:#26;// addFinal:(II)V const #49 = Asciz java/lang/Object; const #50 = Asciz java/lang/System; const #51 = Asciz out; const #52 = Asciz Ljava/io/PrintStream;; const #53 = Asciz java/io/PrintStream; const #54 = Asciz println; const #55 = Asciz (I)V; { //默认构造方法 public LearningClassFile(); Code: Stack=2, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V //将id3的引用推送至栈顶 4: aload_0 //将4推送至栈顶 5: iconst_4 //将4赋值给id3 6: putfield #2; //Field id3:I 9: return LineNumberTable: line 11: 0 //public LearningClassFile() { //对于final类型的实例变量在每个构造方法中都会进行一次初始化。 line 7: 4 // private final int id3 = 4; line 12: 9 //} public LearningClassFile(int, int); Code: Stack=2, Locals=3, Args_size=3 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_4 6: putfield #2; //Field id3:I 9: aload_0 10: iload_1 11: putfield #3; //Field id1:I 14: aload_0 15: pop 16: iload_2 17: putstatic #4; //Field id2:I 20: return LineNumberTable: line 14: 0 //public LearningClassFile(int id1, int id2) { //对于final类型的实例变量在每个构造方法中都会进行一次初始化。 line 7: 4 // private final int id3 = 4; line 15: 9 // this.id1 = id1; line 16: 14 // this.id2 = id2; line 17: 20 //} public void addPub(int, int); Code: Stack=2, Locals=4, Args_size=3 0: iload_1 1: iload_2 2: iadd 3: istore_3 4: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 7: iload_3 8: invokevirtual #6; //Method java/io/PrintStream.println:(I)V 11: return LineNumberTable: line 21: 0 // int result = a + b; line 22: 4 // System.out.println(result); line 23: 11 // } public static void addSta(); Code: Stack=2, Locals=1, Args_size=0 //获取静态变量id2推送至栈顶 0: getstatic #4; //Field id2:I //直接从常量池中取出id4的值5推送至栈顶 3: iconst_5 //执行相加操作 4: iadd //将计算结果推送至栈顶 5: istore_0 //获取静态与out 6: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; //取出计算结果 9: iload_0 //调用println方法 10: invokevirtual #6; //Method java/io/PrintStream.println:(I)V //方法正常结束 13: return LineNumberTable: line 33: 0 // int result = id2 + id4; line 34: 6 // System.out.println(result); line 35: 13 //} public static final void addFinal(int, int); Code: Stack=2, Locals=3, Args_size=2 0: iload_0 1: iload_1 2: iadd 3: istore_2 4: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 7: iload_2 8: invokevirtual #6; //Method java/io/PrintStream.println:(I)V 11: return LineNumberTable: line 38: 0 line 39: 4 line 40: 11 public static void main(java.lang.String[]); Code: Stack=4, Locals=2, Args_size=1 //创建一个LearningClassFile对象,并将对象的引用推送至栈顶 0: new #7; //class LearningClassFile //将对象的引用进行备份推送至栈顶 //使用原有的引用值调用实例方法,现在置于栈顶的引用值的位置将被接下来的操作覆盖。 3: dup //将构造函数中的参数1推送至栈顶 4: iconst_1 5: iconst_2 //执行构造方法 6: invokespecial #8; //Method "<init>":(II)V //将栈顶引用型数值存入第二个本地变量 9: astore_1 10: aload_1 11: iconst_1 12: iconst_2 //调用实例方法 13: invokevirtual #9; //Method addPub:(II)V 16: aload_1 17: iconst_1 18: iconst_2 19: invokespecial #10; //Method addPri:(II)V //调用静态方法 22: invokestatic #11; //Method addSta:()V 25: iconst_1 26: iconst_2 27: invokestatic #12; //Method addFinal:(II)V 30: return LineNumberTable: line 43: 0 // LearningClassFile lcf = new LearningClassFile(1, 2); line 44: 10 // lcf.addPub(1, 2); line 45: 16 // lcf.addPri(1, 2); line 46: 22 // addSta(); line 47: 25 // addFinal(1, 2); line 48: 30 //} }
final变量和static final变量的区别:
(1)实例常量和类常量的区别
(2)初识方式不同:从class字节码来看final修饰的变量会出现在每个构造方法中进行一次初始化;static final类型的变量必须在定义的时候进行初始化。 理解"编译期可知,运行期不变": 编译器可确定调用方法的版本,符合这个标准的方法主要有两种:私有方法,静态方法。
详情请看:深入理解JVM读书笔记--字节码执行引擎。
2、final变量和static final变量的区别:
(1)实例常量和类常量的区别
(2)初始化方式不同:从class字节码来看final修饰的变量会出现在每个构造方法中进行一次初始化;static final类型的变量必须在定义的时候进行初始化。
3、理解"编译期可知,运行期不变":
编译器可确定调用方法的版本,符合这个标准的方法主要有两种:私有方法,静态方法。
关于如何解析Java虚拟机的Class文件就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。