《java虚拟机规范SE7》整理——第四章:Class文件格式

按照《java虚拟机规范SE7》章节顺序整理的笔记。

目录:

  1. ClassFile格式(注:也就是class文件的总结构)
  2. 描述符和签名
  3. 常量池
  4. 字段
  5. 方法
  6. 属性
  7. Java虚拟机代码约束
  8. Class文件校验

第四章:class文件格式

这一章详细的介绍了class文件的格式,包括class文件的格式,class文件具体的内容。这些内容均可以通过自己编写一个简单的类,并使用javap反编译来对照着阅读。


<1>. ClassFile格式

这表示class文件的总结构,也就是说:满足虚拟机规范的class文件的构成。
值得注意的是:

  • 其中u1,u2,u4分别代表这个项打大小为 1,2,4个字节。
  • 其中的cp_info, field_info, method_info, attribute_info表示这个表中项的结构,同时也说明这个项的大小暂时是不确定的。
  • 结构中一共有五个表:常量池表,接口表,字段表,方法表,属性表
    而其中只有常量池表是从1开始索引,而其他表均从0开始索引,所以常量池计数器比实际常量的大小多1。
  • 需要重点提出的是,每个class仅仅是针对当前类,或者他的直接父类,如:
    1.字段表仅仅包含当前类或接口声名的字段,并不包含父类或父接口。
    2.方法表仅仅包含当前类或接口声明的方法,并不包含父类或父接口。
ClassFile { 
	u4 magic;   //魔数,唯一的作用是标志当前class是否符合虚拟机规范,
				//唯一的值为:0xCOFEBABE(咖啡宝宝?)
				
	u2 minor_version;   //副版本号
	u2 major_version;   //主版本号
				//主版本号和副版本号结合起来,作用是表示当前的class是否被某个版本的java虚拟机支持。
				//各个版本的虚拟机都有自己支持的主副版本号范围。
				
	u2 constant_pool_count;   //常量池计数器,值为后面常量池表成员数加1.
	cp_info constant_pool[constant_pool_count-1];   //常量池表,
				//每一项都是cp_info结构,索引从1到constant_pool_count-1。
				
	u2 access_flags;   //访问标志,标志类或接口的访问权限及基础属性(值见下表)。
	u2 this_class;    //类索引,表示当前文件的类或接口,值得注意,类并非一定与文件名相同,如:内部类。
	u2 super_class;   //直接父类
				//如果值为0:表示没有父类,而java中没有父类的只有java.lang.Object
				//如果不是0:表示这个一个类或接口,他的值必须是常量池中的CONSTANT_Class_info的值
				
	u2 interfaces_count;   //接口计数器,大小为当前类直接父接口数量。
	u2 interfaces[interfaces_count];   //接口表
				//每一项的值都是常量池中的CONSTANT_Class_info类型的值。
				//索引从0开始。
	
	u2 fields_count;   //字段计数器 ,大小为当前类或接口所定义的字段。
	field_info fields[fields_count];   //字段表
				//每一项都是field_info结构。索引从0开始。

	u2 methods_count;  //方法计数器,大小为当前类或接口所定义的方法。
	method_info methods[methods_count];   //方法表
				//每一项都是method_info结构。索引从0开始。

	u2 attributes_count;  //属性计数器,大小为class文件中的属性数量。	
	attribute_info attributes[attributes_count];   //属性表
				每一项都是attribute_info结构。索引从0开始。
}

访问标志一览表 (access_flags):

标记名含义
ACC_PUBLIC0x0001可以被包的类外访问
ACC_FINAL0x0010不允许有子类
ACC_SUPER0x0020当用到invokespecial指令时,需要特殊处理的父类方法
ACC_INTERFACE0x0200标识定义的是接口而不是类
ACC_ABSTRACT0x0400不能被实例化
ACC_SYNTHETIC0x1000标识并非Java源码生成的代码
ACC_ANNOTATION0x2000标识注解类型
ACC_ENUM0x4000标识枚举类型

<2>. 描述符和签名

描述符:一个描述字段或方法的类型的字符串。
可分为:

  • 字段描述符
  • 方法描述符

a.字段描述符:描述了字段的类型。

  1. 描述原始类型
字符类型含义
Bbyte有符号字节型数
CcharUnicode字符,UTF-16编码
Ddouble双精度浮点数
Ffloat单精度浮点数
Iint整型数
Jlong长整数
Sshort有符号短整数
Zboolean布尔值 true/false
L Classname;reference一个名为的实例
[reference一个一维数组
  1. 描述对象
    格式:L+Classname
    注:这里的Classname是类的全限定名,如Object类,Ljava.lang.Object

  2. 描述数组
    格式:[ + ComponentType
    例如:[ I , [[java.lang.Object

b. 方法描述符:描述了方法的参数类型,参数大小,和返回类型。
格式:( 使用字段描述符描述的0或n个参数 ) 返回类型同样使用字段描述符表示
例如:方法 int method(int a,Object b) 描述为 ( I [java.lang.Object; ) I ;

注意:对于实例方法中的this参数,并不显示的写在方法描述符中,它是在java虚拟机调用方法的时候隐式传递。

签名:是用于描述字段、方法和类型定义中的泛型信息的字符串。
签名是用于给Java语言使用的描述信息编码,不在Java虚拟机系统使用的类型中。

  1. 类签名,作用是把Class申明的类型信息编译成对应的签名信息。
  2. 字段类型签名,作用是将字段、参数或局部变量的类型编译成对应的签名信息。
  3. 方法签名,作用是将方法中所有的形式参数的类型编译成相应的签名信息(或将它们参数化)。

<3>. 常量池

常量池中每一项都是一个cp_info结构,而cp_info的结构如下:

cp_info { 
	u1 tag; 
	u1 info[];   //这个值的大小由tag标志决定。
}

例如CONSTANT_Class_info 表示为:

CONSTANT_Class_info{
	u1 tag;
	u2 name_index;
}

tag标签一共有14种,如下表:

常量类型
CONSTANT_Class_info7
CONSTANT_Fieldref_info9
CONSTANT_Methodref_info10
CONSTANT_InterfaceMethodref_info11
CONSTANT_String_info8
CONSTANT_Integer_info3
CONSTANT_Float_info4
CONSTANT_Long_info5
CONSTANT_Double_info6
CONSTANT_NameAndType_info12
CONSTANT_Utf8_info1
CONSTANT_MethodHandle_info15
CONSTANT_MethodType_info16
CONSTANT_InvokeDynamic_info18

类 ——CONSTANT_Class_info 格式:

CONSTANT_Class_info{
	u1 tag;  //值为 7
	u2 name_index;  //name_index项的值,必须是对常量池的一个有效索引。
					//常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,
					//代表一个有效的类或接口二进制名称的内部形式。
}

字段,方法,接口方法——CONSTANT_Fieldref_info, CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info的格式相同:

CONSTANT_Fieldref_info { 
	u1 tag;  //值为 9
	u2 class_index;  //class_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,既可以是类也可以是接口。
	u2 name_and_type_index;  //name_and_type_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,必须是字段描述符。
}
CONSTANT_Methodref_info { 
	u1 tag;  //值为 10
	u2 class_index;   //class_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,且必须是类(不能是接口)。
	u2 name_and_type_index; //name_and_type_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,必须是方法描述符
}
CONSTANT_InterfaceMethodref_info { 
	u1 tag;  //值为 11
	u2 class_index; //class_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,且必须是接口(不能是类)。
	u2 name_and_type_index;  //name_and_type_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,必须是方法描述符
}

*字符串——CONSTANT_String_info 格式:

CONSTANT_String_info {
	u1 tag;  //值为 8
	u2 string_index;   //必须是对常量池的有效索引,索引处的项必须是CONSTANT_Utf8_info
}

数值常量——CONSTANT_Integer_info,CONSTANT_Float_info 格式:

CONSTANT_Integer_info { 
	u1 tag; //值为 3 
	u4 bytes; //int类型常量值
} 
CONSTANT_Float_info { 
	u1 tag;  //值为 4
	u4 bytes;  //float类型常量值
}

float类型的值按照下面方法表示,bytes项的值首先被转换成一个int常量的bits:

  • 如果bits值为0x7f800000,表示float值为正无穷。
  • 如果bits值为0xff800000,表示float值为负无穷。
  • 如果bits值在范围0x7f800001到0x7fffffff或者0xff800001到0xffffffff内,表示float值为NaN。
  • 在其它情况下,设s、e、m,它们值根据bits和如下公式计算:
int s =((bits >> 31) == 0) ? 1 : -1; 
int e =((bits >> 23) & 0xff);
int m =(e == 0) ? bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;

则float的浮点值为数值表达式s·m·2^(e–150)的计算结果。

数值常量——CONSTANT_Long_info和CONSTANT_Double_info 格式:

在Class文件的常量池中,所有的8字节的常量都占两个表成员(项)的空间。如果一个CONSTANT_Long_info或CONSTANT_Double_info结构的项在常量池中的索引为n,则常量池中下一个有效的项的索引为n+2,此时常量池中索引为n+1的项有效但必须被认为不可用。

CONSTANT_Long_info { 
	u1 tag;   //值为 5
	u4 high_bytes; 
	u4 low_bytes; 
}
CONSTANT_Double_info { 
	u1 tag;   //值为6
	u4 high_bytes; 
	u4 low_bytes; 
}

Long:high_bytes和low_bytes项用于共同表示long型常量,构造形式为
((long) high_bytes << 32) + low_bytes
也就是将高位在前,地位在后

Double:与上一个Float类似,首先被转换成一个long常量的bits:

  • 如果bits值为0x7ff0000000000000L,表示double值为正无穷。
  • 如果bits值为0xfff0000000000000L,表示double值为负无穷。
  • 如果bits值在范围0x7ff0000000000001L到 0x7fffffffffffffffL或者0xfff0000000000001L到 0xffffffffffffffffL内,表示double值为NaN。
  • 在其它情况下,设s、e、m,它们的值根据bits和如下公式计算:
int s =((bits >> 63) == 0) ? 1 : -1; 
int e =(int)((bits >> 52) & 0x7ffL); 
long m =(e == 0) ? (bits & 0xfffffffffffffL) << 1 : (bits & 0xfffffffffffffL) | 0x10000000000000L;

则double的浮点值为数学表达式s·m·2e – 1075的计算结果。

表示字段或方法——CONSTANT_NameAndType_info 格式:

CONSTANT_NameAndType_info { 
	u1 tag;   //值为 12
	u2 name_index;  //必须是对常量池的有效索引CONSTANT_Utf8_info,这个结构要么表示特殊的方法名<init>,要么表示一个有效的字段或方法的非限定名。
	u2 descriptor_index; //索引处的项必须是CONSTANT_Utf8_info,这个结构表示一个有效的字段描述符或方法描述符。
}

表示字符串常量——CONSTANT_Utf8_info 格式 :

CONSTANT_Utf8_info { 
	u1 tag;   //值为 1
	u2 length;   //指明了bytes[]数组的长度
	u1 bytes[length];  //bytes[]是表示字符串值的byte数组,bytes[]数组中每个成员的byte值都不会是0,也不在0xf0至0xff范围内。
}

方法句柄——CONSTANT_MethodHandle_info 格式:

CONSTANT_MethodHandle_info {
	u1 tag;  //值为 15
	u1 reference_kind;   //值必须在1-9之间,根据这个值的不同,将造成下面项的不同。
	u2 reference_index; 
}

reference的取值:

  • 如果reference_kind项的值为1(REF_getField)、2(REF_getStatic)、3(REF_putField)或4(REF_putStatic),那么常量池在reference_index索引处的项必须是CONSTANT_Fieldref_info(§4.4.2)结构,表示由一个字段创建的方法句柄。
  • 如果reference_kind项的值是5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或8(REF_newInvokeSpecial),那么常量池在reference_index索引处的项必须是CONSTANT_Methodref_info(§4.4.2)结构,表示由类的方法或构造函数创建的方法句柄。
  • 如果reference_kind项的值是9(REF_invokeInterface),那么常量池在reference_index索引处的项必须是CONSTANT_InterfaceMethodref_info(§4.4.2)结构,表示由接口方法创建的方法句柄。
  • 如果reference_kind项的值是5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或9(REF_invokeInterface),那么方法句柄对应的方法不能为实例初始化()方法或类初始化方法()。
  • 如果reference_kind项的值是8(REF_newInvokeSpecial),那么方法句柄对应的方法必须为实例初始化()方法。

表示invokedynamic指令所使用到的引导方法——CONSTANT_InvokeDynamic_info 格式:

CONSTANT_InvokeDynamic_info { 
	u1 tag;   //值为 18
	u2 bootstrap_method_attr_index; 
	u2 name_and_type_index; 
}

<4>. 字段

字段每一项都是一个field_info结构:

field_info { 
	u2 access_flags;   //字段的访问标志,包括访问限制,基础属性
 	u2 name_index;   //必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的字段的非全限定名。
	u2 descriptor_index;   //索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的字段的描述符
	u2 attributes_count;   //字段的属性计数器
	attribute_info attributes[attributes_count];  //attributes表的每一个成员的值必须是attribute结构,一个字段可以有任意个关联属性。
}

字段访问标志一览表:

标记名说明
ACC_PUBLIC0x0001public,表示字段可以从任何包访问。
ACC_PRIVATE0x0002private,表示字段仅能该类自身调用。
ACC_PROTECTED0x0004protected,表示字段可以被子类调用。
ACC_STATIC0x0008static,表示静态字段。
ACC_FINAL0x0010final,表示字段定义后值无法修改(JLS §17.5)。
ACC_VOLATILE0x0040volatile,表示字段是易变的。
ACC_TRANSIENT0x0080transient,表示字段不会被序列化。
ACC_SYNTHETIC0x1000表示字段由编译器自动产生。
ACC_ENUM0x4000enum,表示字段为枚举类型。

<5>. 方法

方法的每一项都是一个method_info结构:

method_info { 
	u2 access_flags;  //方法访问标志
	u2 name_index;  //常量池在该索引处的项必须是CONSTANT_Utf8_info结构,它要么表示初始化方法的名字(<init>或<clinit>),要么表示一个方法的有效的非全限定名。
	u2 descriptor_index;   //。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的方法的描述符 注意:本规范在未来的某个版本中可能会要求当access_flags项的ACC_VARARGS标志被设置时,方法描述符中的最后一个参数必须是数组类型。
	u2 attributes_count;   //方法属性计数器
	attribute_info attributes[attributes_count]; //attributes表的每一个成员的值必须是attribute结构,一个方法可以有任意个与之相关的属性。
}

方法访问标志一览表:

标记名说明
ACC_PUBLIC0x0001public,方法可以从包外访问
ACC_PRIVATE0x0002private,方法只能本类中访问
ACC_PROTECTED0x0004protected,方法在自身和子类可以访问
ACC_STATIC0x0008static,静态方法
ACC_FINAL0x0010final,方法不能被重写(覆盖)
ACC_SYNCHRONIZED0x0020synchronized,方法由管程同步
ACC_BRIDGE0x0040bridge,方法由编译器产生
ACC_VARARGS0x0080表示方法带有变长参数
ACC_NATIVE0x0100native,方法引用非java语言的本地方法
ACC_ABSTRACT0x0400abstract,方法没有具体实现
ACC_STRICT0x0800strictfp,方法使用FP-strict浮点格式
ACC_SYNTHETIC0x1000方法在源文件中不出现,由编译器产生

方法属性表的值可以有:

  • Code
  • Exceptions
  • Synthetic
  • Signature
  • Deprecated
  • untimeVisibleAnnotations
  • RuntimeInvisibleAnnotations
  • RuntimeVisibleParameterAnnotations
  • RuntimeInvisibleParameterAnnotations
  • AnnotationDefault

<6>. 属性

属性的每一项都是attribute_info结构:

attribute_info { 
	u2 attribute_name_index;   //是常量池中的CONSTANT_Utf8_info索引。
	u4 attribute_length;  
	u1 info[attribute_length]; 
}

ConstantValue属性:
位于field_info结构的属性表中,表示一个常量字段的值,在一个field_info结构的属性表中最多只能有一个ConstantValue属性。
格式:

ConstantValue_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 constantvalue_index; 
}

Code属性:
位于method_info结构的属性表,一个Code属性只为唯一一个方法、实例类初始化方法或类初始化方法保存Java虚拟机指令及相关辅助信息。
如果方法被声明为native或者abstract类型,那么对应的method_info结构不能有明确的Code属性。
格式:

Code_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 max_stack; 
	u2 max_locals; 
	u4 code_length; 
	u1 code[code_length]; 
	u2 exception_table_length;
	{ 
		u2 start_pc; 
		u2 end_pc; 
		u2 handler_pc; 
		u2 catch_type; 
	} 
	exception_table[exception_table_length]; 
	u2 attributes_count; 
	attribute_info attributes[attributes_count]; 
}

StackMapTable属性:
位于Code属性的属性表中,这个属性会在虚拟机类加载的类型阶段被使用。
格式:

StackMapTable_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 number_of_entries; 
	stack_map_frame entries[number_of_entries];
}

Exceptions属性:
它位于method_info结构的属性表中,指出了一个方法需要检查的可能抛出的异常。
格式:

Exceptions_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 number_of_exceptions; 
	u2 exception_index_table[number_of_exceptions]; 
}

InnerClasses属性:
位于ClassFile结构的属性表,支持内部类和内部接口而引入。
格式:

InnerClasses_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 number_of_classes;
	{ 
		u2 inner_class_info_index; 
		u2 outer_class_info_index; 
		u2 inner_name_index; 
		u2 inner_class_access_flags;
	} 
	classes[number_of_classes]; 
}

EnclosingMethod属性:
位于ClassFile结构的属性表,且仅当Class为局部类或者匿名类时,才能具有EnclosingMethod属性,一个类中只能有一个。
格式:

EnclosingMethod_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 class_index 
	u2 method_index; 
}

Synthetic属性:
位于ClassFile中的属性表,如果一个类成员没有在源文件中出现,则必须标记带有Synthetic属性,或者设置ACC_SYNTHETIC标志。
格式:

Synthetic_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
}

Signature属性:
位于ClassFile,field_info或method_info结构的属性表中,任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量或参数化类型,则Signature属性会为它记录泛型签名信息。
格式:

Signature_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 signature_index; 
}

SourceFile属性:
位于ClassFile结构的属性表,一个ClassFile结构中的属性表最多只能包含一个SourceFile属性。
格式:

 SourceFile_attribute { 
 	u2 attribute_name_index; 
 	u4 attribute_length; 
 	u2 sourcefile_index; 
 }

SourceDebugExtension 属性:
位于ClassFile(§4.1)结构属性表,最多只能包含一个SourceDebugExtension属性。
格式:

SourceDebugExtension_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u1 debug_extension[attribute_length]; 
}

LineNumberTable属性:
位于Code结构的属性表,用于确定源文件中行号表示的内容在Java虚拟机的code[]数组中对应的部分。
格式:

LineNumberTable_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 line_number_table_length; 
	{ 
		u2 start_pc; 
		u2 line_number; 
	} 
	line_number_table[line_number_table_length]; 
}

LocalVariableTable属性:
位于Code属性的属性表中,用于确定方法在执行过程中局部变量的信息。
格式:

LocalVariableTable_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 local_variable_table_length; 
	{ 
		u2 start_pc; 
		u2 length; 
		u2 name_index; 
		u2 descriptor_index;
		u2 index; 
	} 
	local_variable_table[local_variable_table_length];
}

LocalVariableTypeTable属性:
位于Code的属性表,用于给调试器确定方法在执行中局部变量的信息。
格式:

LocalVariableTypeTable_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 local_variable_type_table_length;
   { 
   	u2 start_pc; 
   	u2 length; 
   	u2 name_index; 
   	u2 signature_index; 
   	u2 index; 
   }
   local_variable_type_table[local_variable_type_table_length]; 
}

Deprecated属性:
位于ClassFile, field_info 或 method_info结构的属性表中,标记了此属性,则说明它将会在后续某个版本中被取代。
格式:

Deprecated_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length;
}

RuntimeVisibleAnnotations属性:
位于ClassFile, field_info或method_info结构的属性表中,用于保存Java语言中的类、字段或方法的运行时的可见注解。
格式:

RuntimeVisibleAnnotations_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 num_annotations; 
	annotation annotations[num_annotations]; 
}

RuntimeInvisibleAnnotations 属性:
位于ClassFile, field_info或method_info结构的属性表中,用于保存Java语言中的类、字段或方法的运行时的非可见注解。
格式:

RuntimeInvisibleAnnotations_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 num_annotations; 
	annotation annotations[num_annotations]; 
}

RuntimeVisibleParameterAnnotations属性:
位于method_info结构的属性表中 ,用于保存对应方法的参数的所有运行时可见Java语言注解
格式:

RuntimeVisibleParameterAnnotations_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u1 num_parameters; 
	{ 
		u2 num_annotations; 
		annotation annotations[num_annotations]; 
	} 
	parameter_annotations[num_parameters]; 
}

RuntimeInvisibleParameterAnnotations属性:
位于method_info结构的属性表中,用于保存对应方法的参数的所有运行时非可见的Java语言注解。
格式:

RuntimeInvisibleParameterAnnotations_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u1 num_parameters; 
	{ 
		u2 num_annotations; 
		annotation annotations[num_annotations]; 
	} 
	parameter_annotations[num_parameters]; 
}

AnnotationDefault属性:
位于某些特殊的method_info结构的属性表中,这些结构表示注解类型的元素,用于保存method_info结构表示的元素的默认值。
格式:

AnnotationDefault_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	element_value default_value; 
}

BootstrapMethods属性:
位于ClassFile结构的属性表中,用于保存invokedynamic指令引用的引导方法限定符。
格式:

BootstrapMethods_attribute { 
	u2 attribute_name_index; 
	u4 attribute_length; 
	u2 num_bootstrap_methods; 
	{  
		u2 bootstrap_method_ref; 
		u2 num_bootstrap_arguments; 
		u2 bootstrap_arguments[num_bootstrap_arguments]; 
	} 
	bootstrap_methods[num_bootstrap_methods]; 
}

<7>. Java虚拟机代码约束

这里使用了很多对java虚拟机的约束,由于内容过多,且对只是为了解一些java虚拟机情况的我来说并不需要深入理解,便不摘录了。


至此,对于javac编译的class文件也有了深入的理解,对java虚拟机内部结构也有了认识。后面的内容主要就是:Java虚拟机如何加载class文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值