深入java虚拟机--Class文件实例解析

本文通过一个示例程序详细解析了Java类文件结构及JVM执行原理,对比final与static final变量的区别,并介绍了编译期可知、运行期不变的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面发了几篇学习笔记,但是看这些东西总是感觉很"玄乎",来一篇实战的东西来揭一下"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. 理解"编译期可知,运行期不变": 编译器可确定调用方法的版本,符合这个标准的方法主要有两种:私有方法,静态方法。详情请看:深入理解JVM读书笔记--字节码执行引擎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值