引用了
前言
1.最近在学习关于虚拟机的知识,整理一份比较详细的class文件结构。
2.翻译class文件就可以称为解释器
3.你可以使用javap -v Hello.class 命令去查看class类文件结构
4.可以从你的目标目录和安装目录的rt.jar中读取基本够用的class文件
读取字节码
private fun getClassBit(goal: String): ByteArrayBean {
var result = getFromJar("/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/rt.jar", goal)
if (result == null) {
result = getFromFile("/Users/haha/Downloads/", goal)
}
return ByteArrayBean(result, 0)
}
private fun getFromJar(path: String, goal: String): ByteArray? {
val zipInputStream = ZipInputStream(FileInputStream(path))
val zipFile = ZipFile(path)
var zipEntry: ZipEntry?
while (true) {
zipEntry = zipInputStream.nextEntry
if (zipEntry == null) {
break
}
if (zipEntry.name == goal.plus(".class")) {
return zipFile?.getInputStream(zipEntry).readBytes()
}
}
return null
}
private fun getFromFile(path: String, goal: String): ByteArray {
val file = File("$path$goal.class")
return file.readBytes()
}
Class类文件结构
| 名称 |
变量 |
长度/Byte |
| 魔数 |
magic |
4 |
| 次版本号 |
minor_version |
2 |
| 主版本号 |
major version |
2 |
| 常量数 |
constant_num |
2 |
| 常量池 |
constant_pool |
常量*(constant_num - 1) |
| 访问标志 |
access_flags |
2 |
| 类索引 |
this_class |
2 |
| 父类索引 |
super_class |
2 |
| 接口数量 |
interface_num |
2 |
| 接口池 |
interface_pool |
接口*(interface_num) |
| 变量数量 |
field_num |
2 |
| 变量池 |
field_pool |
变量*(field_num) |
| 方法数量 |
method_num |
2 |
| 方法池 |
method_pool |
方法*(method_num) |
| 附加属性数量 |
attribute_num |
2 |
| 附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
常量结构
| 名称 |
变量 |
长度/byte |
| 类型 |
type |
1 |
| 内容 |
content |
不固定,由下表决定 |
| 类型值 |
名称 |
格式 |
长度/bytes |
说明 |
| 1 |
UTF8 |
length |
2 |
长度 |
|
|
Text |
length |
内容 |
| 3 |
Int |
|
4 |
值 |
| 4 |
Float |
|
4 |
值 |
| 5 |
Long |
|
8 |
值 |
| 6 |
Double |
|
8 |
值 |
| 7 |
Class |
index |
2 |
指向index位置的utf8常量 |
| 8 |
String |
index |
2 |
指向index位置的utf8常量 |
| 9 |
Fieldref |
class index |
2 |
指向index位置的class常量 |
|
|
nameAndType index |
2 |
指向index位置的nameAndType常量 |
| 10 |
Methodref |
class index |
2 |
指向index位置的class常量 |
|
|
nameAndType index |
2 |
指向index位置的nameAndType常量 |
| 11 |
Interface Methodref |
class index |
2 |
指向index位置的class常量 |
|
|
nameAndType index |
2 |
指向index位置的nameAndType常量 |
| 12 |
NameAndType |
name index |
2 |
指向index位置的utf8常量 |
|
|
type index |
2 |
指向index位置的utf8常量 |
备注:当常量池出现long时,constant_pool应该跳过一位,例如constant_pool[16]=1280977330748,下一位应该给constant_pool[18]赋值,原因未知,double未测试,测试class文件为String.class
接口结构
| 名称 |
长度 |
| Interface Classref Index |
2 |
备注:接口池存的很简单,只是指向常量池中接口类的索引
变量结构
| 名称 |
变量 |
长度/bytes |
| 访问标志 |
access_flags |
2 |
| 变量名索引 |
name_index |
2 |
| 变量类型索引 |
descriptor_index |
2 |
| 附加属性数量 |
attribute_num |
2 |
| 附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
方法结构
| 名称 |
变量 |
长度/bytes |
| 访问标志 |
access_flags |
2 |
| 方法名索引 |
name_index |
2 |
| 方法类型索引 |
descriptor_index |
2 |
| 附加属性数量 |
attribute_num |
2 |
| 附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
附加属性结构
| 名称 |
变量 |
长度/bytes |
| 属性名索引 |
name_index |
2 |
| 长度 |
length |
4 |
| 内容 |
attribute_pool |
length |
根据name_index指向的属性决定,属性bean可能为不同格式,不同长度
方法结构-附加属性-Code属性(属于附加属性,包含附加属性)
| 名称 |
变量 |
长度/bytes |
| 最大栈深度 |
stack_length |
2 |
| 局部变量表大小 |
localproperty_num |
2 |
| 指令长度 |
order_length |
4 |
| 指令集 |
order_array |
order_length |
| 异常数量 |
exception_num |
2 |
| 异常池 |
exception_pool |
异常属性*exception_num |
| 附加属性数量 |
attribute_num |
2 |
| 附加属性池 |
attribute_pool |
附加属性*(attribute_num) |
方法结构-附加属性-Code属性-异常属性
| 名称 |
变量 |
长度/bytes |
| 起始指令位置 |
from |
2 |
| 结束指令位置 |
to |
2 |
| 目标指令位置 |
target |
2 |
| 异常类型 |
type |
2 |
已知附加属性
| 属性名 |
class |
field |
method |
method-code |
| Signature |
* |
* |
* |
|
| Code |
|
|
* |
|
| LineNumberTable |
|
|
|
* |
| LocalVariableTable |
|
|
|
* |
| StackMapTable |
|
|
|
* |
| Deprecated |
|
|
* |
|
| RuntimeVisibleAnnotations |
|
|
* |
|
| Exceptions |
|
|
* |
|
| SourceFile |
* |
|
|
|
| InnerClasses |
* |
|
|
|
类访问标志符

类访问标志符
方法访问标志符

方法访问标志符
网友评论