roboslyq的技术专栏 JavaEE Coder,micro service

Java基础(0)-Class源码分析

2019-04-26
roboslyq


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                           索引编号:60x0007 = 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  			类索引在常量池中位置:000507类型,故使用值0x001A定位到常量池中值为:com/roboslyq/core/common/HelloWorldBean
||0006  父类索引在常量池中位置:000607类型,故使用值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

Similar Posts

Comments