深入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. 理解"编译期可知,运行期不变": 编译器可确定调用方法的版本,符合这个标准的方法主要有两种:私有方法,静态方法。详情请看:深入理解JVM读书笔记--字节码执行引擎


posted @ 2012-03-05 08:15  focusJ  阅读(3217)  评论(1编辑  收藏  举报