Loading

(转)javap反编译字节码详解

找到一篇比较好的介绍Javap的使用方法的文章,特此记录,原文地址:

https://blog.csdn.net/sinat_14913533/article/details/90738019

package com.zjj;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
/**
 * create by zhaojiang02 2019-06-01
 */
public final class ClassFileTest {
    private int privateint = 1;
    private static int privatestaticint = 2;
    private static final int privatestaticfinalint = 2;
 
    public int sayHello(String s) throws IOException {
        System.out.println("hello " + s);
        List<Integer> interfaceRef = new ArrayList();
        ArrayList<Integer> classRef = new ArrayList();
 
        interfaceRef.add(1);
        classRef.add(1);
 
        interfaceRef.stream().forEach(System.out::println);
        classRef.stream().forEach(System.out::println);
 
        invokeStatic();
 
        invokeprivate();
 
        super.hashCode();
 
        try {
            System.out.println("try");
        } catch (RuntimeException e) {
            System.out.println("runtimeexception");
        } finally {
            System.out.println("finally");
        }
 
        return 100;
    }
 
    private static void invokeStatic() {}
 
    private void invokeprivate() {}

}

下面是利用javap命令解析后的class内容,解析命令

javap -v -p  ClassFileTest.class
Classfile /Users/george/working/zjjdemo/javatest/target/classes/com/zjj/ClassFileTest.class #class文件的位置
  Last modified 2019-6-2; size 2767 bytes  # class文件的基本信息,最后修改时间,所占大小字节数
  MD5 checksum 8ed29bfec33883dbfd84fde18936240f # class文件MD5的hash值
  Compiled from "ClassFileTest.java"  # 源文件类

public final class com.zjj.ClassFileTest
  minor version: 0  # 最小版本
  major version: 52 # 最大版本(每个jvm都有能识别的class文件的最小版本和最大版本,超过这个范围,jvm不能进行类的加载)
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER  # 类标识符。本例上,凡是可以在类上的标记都会在这里展示,如static,final等。TestFileTest这个类是public的

Constant pool:
# #1=Methodref  等号签名是常量池的编号,从1开始编号,每个常量项占一个编号。等号后面的是该常量项的类型(jvm定义的14中类型)。
# 第2列。如果常量项是一个引用,指向其他的常量项,那地三列就是该向量项指向那个常量项的编号。
# 第3列就是常量项的符号引用。全类名/字段名/方法名,以及字段或者方法的描述符。

# 比如第一行:第一个常量项是CONSTANT_Methodref_info类型的,它表示一个方法的常量引用,它的就结构中有两个index:
  第一列是指向该方法的类描述符的常量项(CONSTANT_Class_info);
  第二列index是该方法名称及类型的描述符(CONSTANT_NameAndType)。
  第三列就是符号应用:java.lang.Object.<init>的构造方法,()V方法返回值是void,没有形参表。
依次往下找,找到一个完整的方法相关的常量(如下用特殊颜色标注)
    #1 = Methodref          #29.#70       // java/lang/Object."<init>":()V
    #2 = Fieldref           #28.#71       // com/zjj/ClassFileTest.privateint:I
    #3 = Fieldref           #72.#73       // java/lang/System.out:Ljava/io/PrintStream;
    #4 = Class              #74           // java/lang/StringBuilder
    #5 = Methodref          #4.#70        // java/lang/StringBuilder."<init>":()V
    #6 = String             #75           // hello  # 字符串的字面常量,值为“hello”(//后的是备注,实际存放这个字面常量的是一个utf8结构,在75行出)
    #7 = Methodref          #4.#76        // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    #8 = Methodref          #4.#77        // java/lang/StringBuilder.toString:()Ljava/lang/String;
    #9 = Methodref          #78.#79       // java/io/PrintStream.println:(Ljava/lang/String;)V
   #10 = Class              #80           // java/util/ArrayList
   #11 = Methodref          #10.#70       // java/util/ArrayList."<init>":()V
   #12 = Methodref          #81.#82       // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #13 = InterfaceMethodref #83.#84       // java/util/List.add:(Ljava/lang/Object;)Z
   #14 = Methodref          #10.#84       // java/util/ArrayList.add:(Ljava/lang/Object;)Z
   #15 = InterfaceMethodref #83.#85       // java/util/List.stream:()Ljava/util/stream/Stream;
   #16 = Methodref          #29.#86       // java/lang/Object.getClass:()Ljava/lang/Class;
   #17 = InvokeDynamic      #0:#92        // #0:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
   #18 = InterfaceMethodref #93.#94       // java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
   #19 = Methodref          #10.#85       // java/util/ArrayList.stream:()Ljava/util/stream/Stream;
   #20 = Methodref          #28.#95       // com/zjj/ClassFileTest.invokeStatic:()V
   #21 = Methodref          #28.#96       // com/zjj/ClassFileTest.invokeprivate:()V
   #22 = Methodref          #29.#97       // java/lang/Object.hashCode:()I
   #23 = String             #98           // try
   #24 = String             #99           // finally
   #25 = Class              #100          // java/lang/RuntimeException
   #26 = String             #101          // runtimeexception
   #27 = Fieldref           #28.#102      // com/zjj/ClassFileTest.privatestaticint:I
   #28 = Class              #103          // com/zjj/ClassFileTest
   #29 = Class              #104          // java/lang/Object
   #30 = Utf8               privateint
   
# 类型描述符。I:int;J:long;Z:boolean;[:数组,如[J表示long数组;L:对象类型,Ljava/lang/Object表示Object引用类型
# 其他的都是类型的首字母大写。注意boolean,long,引用类型的特殊
   #31 = Utf8               I    
   #32 = Utf8               privatestaticint
   #33 = Utf8               privatestaticfinalint
   #34 = Utf8               ConstantValue
   #35 = Integer            2
   #36 = Utf8               <init>
   #37 = Utf8               ()V
   #38 = Utf8               Code
   #39 = Utf8               LineNumberTable
   #40 = Utf8               LocalVariableTable
   #41 = Utf8               this
   #42 = Utf8               Lcom/zjj/ClassFileTest;
   #43 = Utf8               sayHello
   
# 方法描述符,括号里是形参的类型描述符,括号后是返回这类型描述符
# 如下的方法是:入参是一个String类型的引用,返回值是int类型的方法描述符
   #44 = Utf8               (Ljava/lang/String;)I  
   #45 = Utf8               e # 字符串常量。实际的String字面常量,方法名,类名,字段名都是用utf8结构
   #46 = Utf8               Ljava/lang/RuntimeException;
   #47 = Utf8               s
   #48 = Utf8               Ljava/lang/String;
   #49 = Utf8               interfaceRef
   #50 = Utf8               Ljava/util/List;
   #51 = Utf8               classRef
   #52 = Utf8               Ljava/util/ArrayList;
   #53 = Utf8               LocalVariableTypeTable
   #54 = Utf8               Ljava/util/List<Ljava/lang/Integer;>;
   #55 = Utf8               Ljava/util/ArrayList<Ljava/lang/Integer;>;
   #56 = Utf8               StackMapTable
   #57 = Class              #103          // com/zjj/ClassFileTest
   #58 = Class              #105          // java/lang/String
   #59 = Class              #106          // java/util/List
   #60 = Class              #80           // java/util/ArrayList
   #61 = Class              #100          // java/lang/RuntimeException
   #62 = Class              #107          // java/lang/Throwable
   #63 = Utf8               Exceptions
   #64 = Class              #108          // java/io/IOException
   #65 = Utf8               invokeStatic
   #66 = Utf8               invokeprivate
   #67 = Utf8               <clinit>
   #68 = Utf8               SourceFile
   #69 = Utf8               ClassFileTest.java
   #70 = NameAndType        #36:#37       // "<init>":()V
   #71 = NameAndType        #30:#31       // privateint:I
   #72 = Class              #109          // java/lang/System
   #73 = NameAndType        #110:#111     // out:Ljava/io/PrintStream;
   #74 = Utf8               java/lang/StringBuilder
   #75 = Utf8               hello
   #76 = NameAndType        #112:#113     // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #77 = NameAndType        #114:#115     // toString:()Ljava/lang/String;
   #78 = Class              #116          // java/io/PrintStream
   #79 = NameAndType        #117:#118     // println:(Ljava/lang/String;)V
   #80 = Utf8               java/util/ArrayList
   #81 = Class              #119          // java/lang/Integer
   #82 = NameAndType        #120:#121     // valueOf:(I)Ljava/lang/Integer;
   #83 = Class              #106          // java/util/List
   #84 = NameAndType        #122:#123     // add:(Ljava/lang/Object;)Z
   #85 = NameAndType        #124:#125     // stream:()Ljava/util/stream/Stream;
   #86 = NameAndType        #126:#127     // getClass:()Ljava/lang/Class;
   #87 = Utf8               BootstrapMethods
   #88 = MethodHandle       #6:#128       // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
   #89 = MethodType         #129          //  (Ljava/lang/Object;)V
   #90 = MethodHandle       #5:#130       // invokevirtual java/io/PrintStream.println:(Ljava/lang/Object;)V
   #91 = MethodType         #131          //  (Ljava/lang/Integer;)V
   #92 = NameAndType        #132:#133     // accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
   #93 = Class              #134          // java/util/stream/Stream
   #94 = NameAndType        #135:#136     // forEach:(Ljava/util/function/Consumer;)V
   #95 = NameAndType        #65:#37       // invokeStatic:()V
   #96 = NameAndType        #66:#37       // invokeprivate:()V
   #97 = NameAndType        #137:#138     // hashCode:()I
   #98 = Utf8               try
   #99 = Utf8               finally
  #100 = Utf8               java/lang/RuntimeException
  #101 = Utf8               runtimeexception
  #102 = NameAndType        #32:#31       // privatestaticint:I
  #103 = Utf8               com/zjj/ClassFileTest
  #104 = Utf8               java/lang/Object
  #105 = Utf8               java/lang/String
  #106 = Utf8               java/util/List
  #107 = Utf8               java/lang/Throwable
  #108 = Utf8               java/io/IOException
  #109 = Utf8               java/lang/System
  #110 = Utf8               out
  #111 = Utf8               Ljava/io/PrintStream;
  #112 = Utf8               append
  #113 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #114 = Utf8               toString
  #115 = Utf8               ()Ljava/lang/String;
  #116 = Utf8               java/io/PrintStream
  #117 = Utf8               println
  #118 = Utf8               (Ljava/lang/String;)V
  #119 = Utf8               java/lang/Integer
  #120 = Utf8               valueOf
  #121 = Utf8               (I)Ljava/lang/Integer;
  #122 = Utf8               add
  #123 = Utf8               (Ljava/lang/Object;)Z
  #124 = Utf8               stream
  #125 = Utf8               ()Ljava/util/stream/Stream;
  #126 = Utf8               getClass
  #127 = Utf8               ()Ljava/lang/Class;
  #128 = Methodref          #139.#140     // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #129 = Utf8               (Ljava/lang/Object;)V
  #130 = Methodref          #78.#141      // java/io/PrintStream.println:(Ljava/lang/Object;)V
  #131 = Utf8               (Ljava/lang/Integer;)V
  #132 = Utf8               accept
  #133 = Utf8               (Ljava/io/PrintStream;)Ljava/util/function/Consumer;
  #134 = Utf8               java/util/stream/Stream
  #135 = Utf8               forEach
  #136 = Utf8               (Ljava/util/function/Consumer;)V
  #137 = Utf8               hashCode
  #138 = Utf8               ()I
  #139 = Class              #142          // java/lang/invoke/LambdaMetafactory
  #140 = NameAndType        #143:#147     // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #141 = NameAndType        #117:#129     // println:(Ljava/lang/Object;)V
  #142 = Utf8               java/lang/invoke/LambdaMetafactory
  #143 = Utf8               metafactory
  #144 = Class              #149          // java/lang/invoke/MethodHandles$Lookup
  #145 = Utf8               Lookup
  #146 = Utf8               InnerClasses
  #147 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #148 = Class              #150          // java/lang/invoke/MethodHandles
  #149 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #150 = Utf8               java/lang/invoke/MethodHandles
{

# 如下是字段表
  private int privateint;
    descriptor: I      # 字段描述符,I表示是一个int型的
    flags: ACC_PRIVATE # 字段的标志,如可见性修饰符,static,final等
    
  private static int privatestaticint;
    descriptor: I
    flags: ACC_PRIVATE, ACC_STATIC

  private static final int privatestaticfinalint;
    descriptor: I
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    # javac限制static final字段有ConstantValue属性,该属性记录了字段的值。 这并不是java规范限制的,java规范描述是static字段可以有ConstantValue属性。
    ConstantValue: int 2 

# 如下是方法表
  public com.zjj.ClassFileTest();#方法定义,和类同名,所以是构造方法
    descriptor: ()V  #方法描述符。
    flags: ACC_PUBLIC # 方法的标志位
    # 如下是方法的Code属性,是方法最重要的部分,方法体编译后产生的字节码都放在Code属性中 
    Code:
    # stack=2:运行该方法所需要的最大操作数栈深度是2.
    # locals=1:运行该方法所需要的最大局部方法表的最大slot数是1
    # args_size:该方法的形参个数。如果是实例方法,第一个形参是this引用。
    stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial     #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield          #2                  // Field privateint:I
         9: return
         
    LineNumberTable:
        line 10: 0
        line 11: 4
    
    LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/zjj/ClassFileTest;

  public int sayHello(java.lang.String) throws java.io.IOException;#实例方法
    descriptor: (Ljava/lang/String;)I
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=6, args_size=2
      # 第一列是字节码偏移量
      # 第二列是字节码助记符
      # 第三列是字节码操作数,#3表示引用了常量池编号为3的项。
      # 第四列可以理解是一个注释。注解giant指令完成的事情
      0: getstatic     #3        // Field java/lang/System.out:Ljava/io/PrintStream; #获取类的static变量的值,并压入栈顶。这里其实是获得PrintStream的类名
      
      #new 是创建对象,如果对应的类没有加载,将触发类加载过程,new是为对象分配内存等初始化属性的前面过程。注意invokespecial调用构造方法已经是在初始化对象了。 
      3: new           #4        // class java/lang/StringBuilder # 字符串+,编译后new了一个StringBuilder来完成字符串的拼接的
      
      #dup:将栈顶元素复制一份压入栈顶
      6: dup
      
      7: invokespecial #5        // Method java/lang/StringBuilder."<init>":()V  #调用构造方法,这里调用的是StringBuilder的构造方法
        
      #ldc:从运行时常量区提取常量压入操作数栈栈顶。
      # #6表示提取的常量是class文件的常量池的第6项常量,查看常量池发现第6项是一个utf8结构,值就是“hello”
      # class文件的常量池中的内容,类加载后会放到运行时方法区的常量区。所以这里就是从运行时方法区的常量池将”hello”这个
      # 字符串压入到操作数栈栈顶。
      10: ldc           #6                  // String hello 
      
      # invokevirtual调用实例方法,#7指向常量池的method_ref,是一个方法引用结构。
      12: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      15: aload_1 #将局部方法表的第1个slot中的引用类型值压入栈顶

      16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      19: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

      22: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

      25: new           #10                 // class java/util/ArrayList

      28: dup

      29: invokespecial #11                 // Method java/util/ArrayList."<init>":()V

      32: astore_2 #将操作数栈栈顶引用类型元素出栈,放到局部变量表的第2个slot中

      33: new           #10                 // class java/util/ArrayList

      36: dup

      #invokespecial指令调用构造方法,private方法,父类方法(super.方法名)也是通过invokespecial调用
      37: invokespecial #11                 // Method java/util/ArrayList."<init>”:()V

      40: astore_3

      41: aload_2

      42: iconst_1  # 将int型的常量1压入到操作数栈的栈顶

      43: invokestatic  #12                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

      #调用接口方法(接口名.方法名),具体执行该指令时,该指令会查找实现该接口的实现类对应的方法
      46: invokeinterface #13,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z

      51: pop # 弹出操作数栈栈顶元素

      52: aload_3

      53: iconst_1

      54: invokestatic  #12                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;#invokestatic调用static方法

      57: invokevirtual #14                 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z

      60: pop

      61: aload_2

      62: invokeinterface #15,  1           // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;

      67: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

      70: dup

      71: invokevirtual #16                 // Method java/lang/Object.getClass:()Ljava/lang/Class;

      74: pop

      #invokedynamic 动态指令,java8中的lambda表达式会编译成invokedynamic指令
      75: invokedynamic #17,  0             // InvokeDynamic #0:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;

      80: invokeinterface #18,  2           // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V

      85: aload_3

      86: invokevirtual #19                 // Method java/util/ArrayList.stream:()Ljava/util/stream/Stream;

      89: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

      92: dup

      93: invokevirtual #16                 // Method java/lang/Object.getClass:()Ljava/lang/Class;

      96: pop

      97: invokedynamic #17,  0             // InvokeDynamic #0:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;

     102: invokeinterface #18,  2           // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V

     107: invokestatic  #20                 // Method invokeStatic:()V

     110: aload_0

     111: invokespecial #21                 // Method invokeprivate:()V

     114: aload_0

     115: invokespecial #22                 // Method java/lang/Object.hashCode:()I

     118: pop

     119: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

     122: ldc           #23                 // String try

     124: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

     127: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

     130: ldc           #24                 // String finally

     132: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

     135: goto          172  #goto 无条件跳转。跳转到172位置的指令执行

     138: astore        4

     140: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

     143: ldc           #26                 // String runtimeexception

     145: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

     148: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

     151: ldc           #24                 // String finally

     153: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

     156: goto          172

     159: astore        5

     161: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

     164: ldc           #24                 // String finally

     166: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

     169: aload         5 # aload index和aload_<n> 一样的,都是将局部变量表的第index/n个slot引用型变量压入操作数栈栈顶

     171: athrow   #抛出一个异常

     172: bipush        100  #bipush将一个byte型数据入栈。这里就是将100压栈

     174: ireturn  #返回栈顶的int型数据,在xxreturn指令的前一个压栈指令,其实就是为返回作准备的

   Exception table:#异常表
   #from to target都是Code属性中的字节码偏移量(Code属性第一列)
   #表示当在执行从from到to之间的字节码的时候,如果抛出type指定的异常,就无条件挑战到target出的字节指令往下执行
   from    to  target type
   119   127   138   Class java/lang/RuntimeException
   119   127   159   any
   138   148   159   any
   159   161   159   any

   LineNumberTable:
        line 16: 0 # 16是源码的行号。0是该源码行对应的字节码开始偏移量。所以源码的第16行含义成指令是0~24(对应Code属性的第一列的值)
        line 17: 25
        line 18: 33
        line 20: 41
        line 21: 52
        line 23: 61
        line 24: 85
        line 26: 107
        line 28: 110
        line 30: 114
        line 33: 119
        line 37: 127
        line 38: 135
        line 34: 138
        line 35: 140
        line 37: 148
        line 38: 156
        line 37: 159
        line 40: 172

   LocalVariableTable:#栈帧局部变量表中的变量和源码中定义的变量之间的关系
   #star和length:说明该变量的生命周期,生命期是从第start个指令开始,往后数lenght个长度的指令。
   #slot:只是改变量在局部变量表中slot的编号。
   #name:局部变量名称
   #signature: 可以认为是变量类型描述符
   Start  Length  Slot  Name   Signature
   140       8     4     e   Ljava/lang/RuntimeException;
   0     175     0  this   Lcom/zjj/ClassFileTest;
   0     175     1     s   Ljava/lang/String;
   33     142     2 interfaceRef   Ljava/util/List;
   41     134     3 classRef   Ljava/util/ArrayList;

   LocalVariableTypeTable:#有泛型的时候会有该属性。由于擦除,方法的描述符不能准确描述泛型类型,所以用这个属性来记录源码中泛型的实际类型
   Start  Length  Slot  Name   Signature
   33     142     2 interfaceRef   Ljava/util/List<Ljava/lang/Integer;>;
   41     134     3 classRef   Ljava/util/ArrayList<Ljava/lang/Integer;>;

   StackMapTable: number_of_entries = 3  #验证阶段用于类型验证优化使用
     frame_type = 255 /* full_frame */
     offset_delta = 138
     locals = [ class com/zjj/ClassFileTest, class java/lang/String, class java/util/List, class java/util/ArrayList ]
     stack = [ class java/lang/RuntimeException ]
     frame_type = 84 /* same_locals_1_stack_item */
     stack = [ class java/lang/Throwable ]
     frame_type = 12 /* same */

   Exceptions:#Exceptions属性,方法有throws申明的时候会有该属性
     throws java.io.IOException

  private static void invokeStatic();
    descriptor: ()V
    flags: ACC_PRIVATE, ACC_STATIC
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 45: 0

  private void invokeprivate();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 49: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
        0       1     0  this   Lcom/zjj/ClassFileTest;

  static {};#static变量初始化以及static块对应的<cinit>方法
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: iconst_2
         1: putstatic     #27                 // Field privatestaticint:I
         4: return
      LineNumberTable:
        line 12: 0
}

SourceFile: "ClassFileTest.java" #class文件的SourceFile属性

InnerClasses:
     public static final #145= #144 of #148; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

BootstrapMethods:#class文件的BootstrapMethods属性

# 每一项都是一个启动方法。
# 第1列是编号,从0开始
# 第2列是指向常量池的引用,它指向的是一个CONSTANT_DynamicInvoke_info的接口。
# 第3列是一个方法调用指令,就是调用引导方法。
# 源码:classRef.stream().forEach(System.out::println); classRef=ArrayList<Interger>

#LambdaMetafactory.metafactory(MethodHandle#LookU[,String,MethodType, MethodType, MethodHandle MethodType)CallSite  调用这个方法,返回一个CallSite引用

# 调用该方法的参数(BootStrapMethods属性的bootstrap_mthods属性的bootstrapmethod_arguments中存储了这些参数)

  0: #88 invoke static java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

    Method arguments:
      #89 (Ljava/lang/Object;)V
      #90 invokevirtual java/io/PrintStream.println:(Ljava/lang/Object;)V
      #91 (Ljava/lang/Integer;)V

#############################invokedynamic相关字节码单拿出来看################################
源码:
public class Main {
    public static void main(String[] args) {
        Runnable r = ()->{
            System.out.println("hello");
        };
    }
}

#字节码:只是将和lambda表达式相关的BootStrapMethods属性以及CONSTANT_DynamicInvoke长两项单独列出来了
Constant pool:()
   #1 = Methodref          #7.#24         // java/lang/Object."<init>":()V
   #2 = InvokeDynamic      #0:#29         // #0:run:()Ljava/lang/Runnable;
   #3 = Fieldref           #30.#31        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = String             #32            // hello
   #5 = Methodref          #33.#34        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #35            // com/zjj/Main
   #7 = Class              #36            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lcom/zjj/Main;
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               r
  #20 = Utf8               Ljava/lang/Runnable;
  #21 = Utf8               lambda$main$0
  #22 = Utf8               SourceFile
  #23 = Utf8               Main.java
  #24 = NameAndType        #8:#9          // "<init>":()V
  #25 = Utf8               BootstrapMethods
  #26 = MethodHandle       #6:#37         // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #27 = MethodType         #9             //  ()V
  #28 = MethodHandle       #6:#38         // invokestatic com/zjj/Main.lambda$main$0:()V
  #29 = NameAndType        #39:#40        // run:()Ljava/lang/Runnable;
  #30 = Class              #41            // java/lang/System
  #31 = NameAndType        #42:#43        // out:Ljava/io/PrintStream;
  #32 = Utf8               hello
  #33 = Class              #44            // java/io/PrintStream
  #34 = NameAndType        #45:#46        // println:(Ljava/lang/String;)V
  #35 = Utf8               com/zjj/Main
  #36 = Utf8               java/lang/Object
  #37 = Methodref          #47.#48        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #38 = Methodref          #6.#49         // com/zjj/Main.lambda$main$0:()V
  #39 = Utf8               run
  #40 = Utf8               ()Ljava/lang/Runnable;
  #41 = Utf8               java/lang/System
  #42 = Utf8               out
  #43 = Utf8               Ljava/io/PrintStream;
  #44 = Utf8               java/io/PrintStream
  #45 = Utf8               println
  #46 = Utf8               (Ljava/lang/String;)V
  #47 = Class              #50            // java/lang/invoke/LambdaMetafactory
  #48 = NameAndType        #51:#55        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #49 = NameAndType        #21:#9         // lambda$main$0:()V
  #50 = Utf8               java/lang/invoke/LambdaMetafactory
  #51 = Utf8               metafactory
  #52 = Class              #57            // java/lang/invoke/MethodHandles$Lookup
  #53 = Utf8               Lookup
  #54 = Utf8               InnerClasses
  #55 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #56 = Class              #58            // java/lang/invoke/MethodHandles
  #57 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #58 = Utf8               java/lang/invoke/MethodHandles  

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         5: astore_1
         6: return
      LineNumberTable:
        line 6: 0
        line 9: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  args   [Ljava/lang/String;
            6       1     1     r   Ljava/lang/Runnable;
            
  private static void lambda$main$0();#lambda表达式,编译器生成了一个方法,器内容就是lambda表达式中的代码编译后的字节码。
    descriptor: ()V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #4                  // String hello
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8

BootstrapMethods:
  #0 : #26 这一长串翻译过来就是在Main这个类中调用了静态方法LambdaMetafactory.metafactory()方法(这个方法的定义和作用见jdk描述)

  0: #26 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

    Method arguments:
      #27 ()V
      #28 invokestatic com/zjj/Main.lambda$main$0:()V    #调用静态方法Main.lambda$main$0:()V ,这个方法是有lambda表达式编译器自动生成的。
      #27 ()V
————————————————
原文链接:https://blog.csdn.net/sinat_14913533/article/details/90738019
posted @ 2021-03-16 19:56  iwasjoker  阅读(291)  评论(0)    收藏  举报