源码和实际运行的字节码之间隔着 javac 的「脱糖」。字符串 switch、枚举、字符串拼接、lambda、finally 到底变成了什么?这篇用 javap 逐个编译、逐个反汇编,看清每一种写法背后的真实字节码。
javap -v 的头部: ['minor version: 0', 'major version: 65', 'flags: (0x0021) ACC_PUBLIC, ACC_SUPER', 'this_class: #89 // T', 'super_class: #2 // java/lang/Object']
常量池 252 项,按类型: {'Utf8': 136, 'NameAndType': 34, 'Class': 25, 'Methodref': 22, 'String': 14, 'InterfaceMethodref': 7, 'MethodHandle': 5, 'InvokeDynamic': 4}
int add(int a, int b) { int s = a + b; return s * 2; } 的字节码: iload_1 ; iload_2 ; iadd ; istore_3 ; iload_3 ; iconst_2 ; imul ; ireturn
默认 LineNumberTable 19 处,LocalVariableTable 0 处,MethodParameters 0 处;文件大小 4861 字节
javac -g LineNumberTable 19 处,LocalVariableTable 16 处,MethodParameters 0 处;文件大小 5632 字节
javac -parameters LineNumberTable 20 处,LocalVariableTable 0 处,MethodParameters 13 处;文件大小 5065 字节 字符串 switch: aload_0 ; astore_1 ; iconst_m1 ; istore_2 ; aload_1 ; invokevirtual #7 // Method java/lang/String.hashCode:()I ; lookupswitch { // 2 ; 50 ; 36 ; aload_1 ; ldc #13 // String alpha ; invokevirtual #15 // Method java/lang/String.equals:(Ljava/lang/Object;)Z ; ifeq 61 ; iconst_0 …
字符串 switch 里调用 hashCode / equals / lookupswitch 的次数: 1 2 2
枚举 switch: aload_0 ; invokevirtual #27 // Method T$Color.ordinal:()I ; lookupswitch { // 2 ; 32 ; 34 ; iconst_1 ; ireturn ; iconst_2 …
枚举 switch 生成的合成类: ['T$1Local.class', 'T$Color.class', 'T$Point.class']
模式 switch: invokedynamic #38, 0 // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I ; tableswitch { // 0 to 2 ; checkcast #42 // class java/lang/Integer ; checkcast #42 // class java/lang/Integer ; checkcast #8 // class java/lang/String …
BootstrapMethods 里的引导方法: ['LambdaMetafactory.metafactory', 'StringConcatFactory.makeConcatWithConstants', 'SwitchBootstraps.typeSwitch']
record Point 的 toString 字节码: aload_0 ; invokedynamic #16, 0 // InvokeDynamic #0:toString:(LT$Point;)Ljava/lang/String; ; areturnJDK 21 javac 默认: aload_0 ; iload_1 ; invokedynamic #7, 0 // InvokeDynamic #0:makeConcatWithConsta ; areturn javac -XDstringConcat=inline: new #7 // class java/lang/StringBuild ; dup ; invokespecial #9 // Method java/lang/StringBuil ; ldc #10 // String a= ; invokevirtual #12 // Method java/lang/StringBuil ; aload_0 ; invokevirtual #12 // Method java/lang/StringBuil ; ldc #16 // String ,b= ; invokevirtual #12 // Method java/lang/StringBuil ; iload_1 ; invokevirtual #18 // Method java/lang/StringBuil ; invokevirtual #21 // Method java/lang/StringBuil ; areturn javac --release 8: 13 条指令,invokedynamic 0 次,invokevirtual StringBuilder.append 4 次 三种写法的指令数:默认 4,inline 13,--release 8 13;class 文件大小:默认 685,inline 497,--release 8 497 字节 invokedynamic 的方法类型与配方: ['Ljava/lang/String;I', 'Ljava/lang/String;I'] []
方法签名里的合成/生成成员: ['static final boolean $assertionsDisabled;', 'private static void lambda$twr$1() throws java.lang.Exception;', 'private static int lambda$lambda$0(int, java.lang.Integer);'] lambda 所在方法里: invokeinterface #59, 1 // InterfaceMethod java/util/List.stream:()Ljava/uti ; iload_1 ; invokedynamic #65, 0 // InvokeDynamic #2:applyAsInt:(I)Ljava/util/funct ; invokeinterface #69, 2 // InterfaceMethod java/util/stream/Stream.mapToInt: ; invokeinterface #75, 1 // InterfaceMethod java/util/stream/IntStream.sum:() lambda 体被编译成的私有静态方法: ['private static void lambda$twr$1() throws java.lang.Exception', 'private static int lambda$lambda$0(int, java.lang.Integer)'] 内部类文件: ['T$1Local.class', 'T$Color.class', 'T$Point.class'] assert 语句: getstatic #88 // Field $assertions ; ifne 20 ; iload_0 ; ifgt 20 ; new #94 // class java/lang/A ; dup ; ldc #96 // String x must be … 装箱/拆箱: ['#44 // Method java/lang/Integer.intValue:()I', '#115 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;', '#44 // Method java/lang/Integer.intValue:()I'] try/finally(finally 里的 println 被复制到几处):println 调用 2 处;athrow 1 个;字节码 14 条 synchronized:monitorenter 1 条,monitorexit 2 条(正常路径与异常路径各一个) try-with-resources:close() 调用 3 处;addSuppressed 1 处