zoukankan      html  css  js  c++  java
  • 深入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
       20return
      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
       11return
      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
       //方法正常结束
       13return
      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
       11return
      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
       30return
      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. 理解"编译期可知,运行期不变": 编译器可确定调用方法的版本,符合这个标准的方法主要有两种:私有方法,静态方法。详情请看:深入理解JVM读书笔记--字节码执行引擎

  • 相关阅读:
    prev()方法使用的注意点
    JS 获取图片的高度
    渐变色
    JS获取时间
    监听鼠标上下滚动事件
    几种常见的边框样式
    左侧导航背景颜色随机变化
    apache开启gzip压缩
    dedecms在linux上安装提示没权限解决办法
    阿里云centos7.3安装lamp环境
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959628.html
Copyright © 2011-2022 走看看