1.Java源码
package com.roboslyq.core.common;
public class HelloWorldBean {
public void sayHello(){
System.out.println("hello,world");
}
public void sayHello2(){
System.out.println("hello,world");
}
public void sayHello1(){
System.out.println("hello,world");
}
public void sayHello3(){
System.out.println("hello,world");
}
}
编译就不说了,直接放入IDEA或者javac 都可以获取相应的Class源文件。
2. 获取Class文件内容
这一步有很多方式可以获取,我选择了比较简单的一种,直接使用文件流读取,然后转换成16进制。最后在进行分析。
/**
* Copyright (C), 2015-2019
* FileName: PrintClass
* Author: luo.yongqian
* Date: 2019/4/23 14:38
* Description: 直接使用流打印Class内容
* History:
* <author> <time> <version> <desc>
* luo.yongqian 2019/4/23 14:38 1.0.0 创建
*/
package com.roboslyq.core.bytecode;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
*
* 〈直接使用流打印Class内容〉
* @author luo.yongqian
* @create 2019/4/23
* @since 1.0.0
*/
public class PrintClass {
public static void main(String[] args) throws IOException {
//Class文件保存路径(根据实际情况自己选定)
File file = new File("core\\target\\classes\\com\\roboslyq\\core\\common\\HelloWorldBean.class");
try( FileInputStream isr = new FileInputStream(file);){
byte[] fileContext = isr.readAllBytes();
//此处输入为Class文件原内容(十六进制展示)
System.out.println(str2HexStr(fileContext));
//测试将十六进制转换为普通字符串(翻译),即可以根据需要将读出Class内容进行翻译
//System.out.println(str2HexStr("sayHello2".getBytes()));
}
}
/**
* 将字符串转换为16进制
* @param bs
* @return
*/
private static String str2HexStr(byte[] bs) {
char[] chars = "0123456789ABCDEF".toCharArray();
StringBuilder sb = new StringBuilder("");
int bit;
for (int i = 0; i < bs.length; i++) {
bit = (bs[i] & 0x0f0) >> 4;
sb.append(chars[bit]);
bit = bs[i] & 0x0f;
sb.append(chars[bit]);
}
return sb.toString();
}
/**
* 将十六进制转换成字符串
* @param srcStr
* @return
*/
public static String hexStringToString(String srcStr) {
if (srcStr == null || srcStr.equals("")) {
return null;
}
srcStr = srcStr.replace(" ", "");
byte[] baKeyword = new byte[srcStr.length() / 2];
for (int i = 0; i < baKeyword.length; i++) {
try {
baKeyword[i] = (byte) (0xff & Integer.parseInt(srcStr.substring(i * 2, i * 2 + 2), 16));
} catch (Exception e) {
e.printStackTrace();
}
}
try {
srcStr = new String(baKeyword, StandardCharsets.UTF_8);
} catch (Exception e1) {
e1.printStackTrace();
}
return srcStr;
}
}
打印日志
CAFEBABE0000003500220A0006001409001500160800170A0018001907001A07001B0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C65010004746869730100294C636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E3B01000873617948656C6C6F01000973617948656C6C6F3201000973617948656C6C6F3101000973617948656C6C6F3301000A536F7572636546696C6501001348656C6C6F576F726C644265616E2E6A6176610C0007000807001C0C001D001E01000B68656C6C6F2C776F726C6407001F0C00200021010027636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E0100106A6176612F6C616E672F4F626A6563740100106A6176612F6C616E672F53797374656D0100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0100136A6176612F696F2F5072696E7453747265616D0100077072696E746C6E010015284C6A6176612F6C616E672F537472696E673B2956002100050006000000000005000100070008000100090000002F00010001000000052AB70001B100000002000A00000006000100000003000B0000000C000100000005000C000D00000001000E000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000500080006000B0000000C000100000009000C000D00000001000F000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000800080009000B0000000C000100000009000C000D000000010010000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000B0008000C000B0000000C000100000009000C000D000000010011000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000E0008000F000B0000000C000100000009000C000D000000010012000000020013
上面的输出16进制即为Class文件中具体内容。
3. 源码分析
约定:使用双竖线“ ”进行比较大的模块分割,单竖线“ ”进行一个模块内的单元分割。“*”在模块内进行更好的分割。
||CAFEBABE 魔数
||0000 小版本号
||0035 主版本号
||0022 常量个数
---------------------常量池开始-----------------------
||0A|0006|0014 索引编号:1:0x000A = 10 表示 CONSTANT_methodref_info,
||09|0015|0016 索引编号:2
||08|0017 索引编号:3
||0A|0018|0019 索引编号:4
||07|001A 索引编号:5
||07|001B 索引编号:6:0x0007 = 7,表示CONSTANT_class_info,
||01|0006|3C696E69743E 索引编号:7 :,值为:<init>
||01|0003|282956 索引编号:8 :,值为:()V
||01|0004|436F6465 索引编号:9 :,值为:Code
||01|000F|4C696E654E756D6265725461626C65 索引编号:10 :,值为:LineNumberTable
||01|0012|4C6F63616C5661726961626C655461626C65 索引编号0x0B = 11 ,值为:LocalVariableTable
||01|0004|74686973 索引编号0x0C = 12 ,值为:this
||01|0029|4C636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E3B 索引编号0x0D = 13 ,值为:Lcom/roboslyq/core/common/HelloWorldBean;
||01|0008|73617948656C6C6F 索引编号0x0E = 14 ,值为:sayHello
||01|0009|73617948656C6C6F32 索引编号0x0F = 15 ,值为:sayHello2
||01|0009|73617948656C6C6F31 索引编号0x10 = 16 ,值为:sayHello1
||01|0009|73617948656C6C6F33 索引编号0x11 = 17 ,值为:sayHello3
||01|000A|536F7572636546696C65 索引编号0x12 = 18 ,值为:SourceFile
||01|0013|48656C6C6F576F726C644265616E2E6A617661 索引编号0x13 = 19,值为:HelloWorldBean.java
||0C|0007|0008
||07|001C
||0C|001D|001E
||01|000B|68656C6C6F2C776F726C64
||07|001F
||0C|0020|0021
||01|0027|636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E 索引编号:0x001A=26,值为com/roboslyq/core/common/HelloWorldBean
||01|0010|6A6176612F6C616E672F4F626A656374 索引编号:0x001A=27,值为java/lang/Object
||01|0010|6A6176612F6C616E672F53797374656D 索引编号:0x001B=28,值为java/lang/System
||01|0003|6F7574
||01|0015|4C6A6176612F696F2F5072696E7453747265616D3B
||01|0013|6A6176612F696F2F5072696E7453747265616D
||01|0007|7072696E746C6E
||01|0015|284C6A6176612F6C616E672F537472696E673B2956
-------------------常量池结束---------------------------------------
||0021 ACC_FLAG:访问标识符(Bit思想)
||0005 类索引在常量池中位置:0005为07类型,故使用值0x001A定位到常量池中值为:com/roboslyq/core/common/HelloWorldBean
||0006 父类索引在常量池中位置:0006为07类型,故使用值0x001B定位到常量池中值为:java/lang/Object,表明父类为Object类。
||0000 接口计数器(表示没有实现接口)
||0000 字段表容量计数器(0x0000 表示没有字段)
||0005 方法表计数器(表明有5个方法:4个方法+1个默认构造函数)
------------------方法1:构造函数方法开始---------------------------------------
||0001 access_flag: 0001,表示public
|0007 name_index: 0007,指向常量池中的#7位置,而#7最终值为"<init>",所以这是个构造方法
|0008 descriptor_index:指向常量池中的#8位置,而#8最终值为"()V",表示返回类型为void
|0001 u2:attributes_count属性个数
------------------构造函数属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009 u2:attribute_name_index:属性类型常量池索引:0009=#9,指向Code部分,表示接下来是Code的数据结构
|0000002F attribute_length: 0x002F = 47(整个Code属性表长度,一直到文中下面|0000*0005*000C*000D*0000结束,刚好47字节)
|0001 max_stack
|0001 max_locals 局部变量表所需要的存储空间,这里是0001=1
|00000005 code_length:字节码指令的长度,这里是00000005,即5,表示接下来的5个字节(2A-B1)都是字节码指令
|2AB70001B1 code
|0000 exception_table_length 异常表的长度,接下来都是异常表的信息,这里没有定义异常,所以是0000 start_pc->u2(占2个字节):没有定义异常,无
end_pc->u2(占2个字节):没有定义异常,无
handler_pc->u2(占2个字节):没有定义异常,无
catch_pc->u2(占2个字节):没有定义异常,无
|0002 attributes_count
------------------LineNumberTable属性表开始---------------------------------------
|000A U2:attribute_name_index:属性类型常量池索引:指向常量池中#10的位置,为“LineNumberTable”
|00000006 U4:attribute_length:属性长度为6(表示接下来6个字节来表示相关信息)
|0001 U2:Line_Number_table_length:值为1,表示Lint_number_table的长度
|00000003 U2-1:字节码行号 U2-2:源码行号
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B U2:attribute_name_index:属性类型常量池索引:指向常量池中#11的位置,值为:LocalVariableTable
|0000000C U4:attribute_length:属性长度为C
|0001 U2:Local_Variable_table_length:值为1,表示只有一个Local_Variable_info
|0000*0005*000C*000D*0000 Local_Variable_info信息:start_pc*length*name_index(常量池位置)*descript_index(值为Lcom/roboslyq/core/common/HelloWorldBean;)*index
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法1:构造函数方法结束---------------------------------------
------------------方法2:sayHello()方法开始---------------------------------------
||0001 0001,表示public
|000E 000E,索引编号0x000E = 14 ,值为sayHello
|0008 指向常量池中的#8位置,而#8最终值为"()V",表示返回类型为void
|0001 属性个数
------------------sayHello()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009 属性类型常量池索引:0x0009=#9,指向Code部分,表示接下来是Code的数据结构
|00000037 整个属性表长度 0x0037 = 3*16 + 7 = 55
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002 Code属性表属性个数 attributes_count
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002 U2:Line_Number_table_length:值为2,表示Lint_number_table的长度
|00000005
|00080006
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B U2:attribute_name_index:属性类型常量池索引:指向常量池中#11的位置,值为:LocalVariableTable
|0000000C U4:attribute_length:属性长度为C
|0001 U2:Local_Variable_table_length:值为1,表示只有一个Local_Variable_info
|0000*0009*000C*000D*0000 Local_Variable_info信息:start_pc*length*name_index(常量池位置)*descript_index(值为Lcom/roboslyq/core/common/HelloWorldBean;)*index
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法2:sayHello()方法结束---------------------------------------
------------------方法3:sayHello2()方法开始---------------------------------------
||0001
|000F
|0008
|0001
------------------sayHello2()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009
|00000037
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002
|00000008
|00080009
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B
|0000000C
|0001
|0000*0009*000C*000D*0000
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法3:sayHello2()方法结束---------------------------------------
------------------方法4:sayHello1()方法开始---------------------------------------
||0001
|0010 索引编号0x0E = 16 ,值为:sayHello1
|0008
|0001
------------------sayHello1()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009
|00000037
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002
|0000000B
|0008000C
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B
|0000000C
|0001
|0000*0009*000C*000D*0000
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法4:sayHello1()方法结束---------------------------------------
------------------方法5:sayHello3()方法开始---------------------------------------
||0001
|0011 索引编号0x11 = 17 ,值为:sayHello3
|0008
|0001
------------------sayHello3()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009
|00000037
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002
|0000000E
|0008000F
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B
|0000000C
|0001
|0000*0009*000C*000D*0000
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法5:sayHello3()方法结束---------------------------------------
00010012000000020013