使用javap反汇编的Enum不';不显示构造函数参数
当我用javap反汇编一个enum时,enum的隐式构造函数参数似乎丢失了,我不知道为什么 下面是一个枚举:使用javap反汇编的Enum不';不显示构造函数参数,java,enums,.class-file,javap,ecj,Java,Enums,.class File,Javap,Ecj,当我用javap反汇编一个enum时,enum的隐式构造函数参数似乎丢失了,我不知道为什么 下面是一个枚举: enum Foo { X } 我使用以下命令编译和反汇编(在Java 8u60上): javac Foo.java && javap -c -p Foo 这是我得到的结果: final class Foo extends java.lang.Enum<Foo> { public static final Foo X; private static
enum Foo { X }
我使用以下命令编译和反汇编(在Java 8u60上):
javac Foo.java && javap -c -p Foo
这是我得到的结果:
final class Foo extends java.lang.Enum<Foo> {
public static final Foo X;
private static final Foo[] $VALUES;
public static Foo[] values();
Code:
0: getstatic #1 // Field $VALUES:[LFoo;
3: invokevirtual #2 // Method "[LFoo;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LFoo;"
9: areturn
public static Foo valueOf(java.lang.String);
Code:
0: ldc #4 // class Foo
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class Foo
9: areturn
private Foo(); // <--- here
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: return
static {};
Code:
0: new #4 // class Foo
3: dup
4: ldc #7 // String X
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field X:LFoo;
13: iconst_1
14: anewarray #4 // class Foo
17: dup
18: iconst_0
19: getstatic #9 // Field X:LFoo;
22: aastore
23: putstatic #1 // Field $VALUES:[LFoo;
26: return
}
我的问题是:javac编译的枚举和Eclipse编译的枚举在物理上有什么不同,导致javap不显示javac编译的枚举的构造函数参数?这种差异是bug吗(在javap、javac或Eclipse中)?类文件中方法的参数和返回类型由一个 随着1.5中泛型的引入。类文件格式中引入了其他信息,即 “方法描述符”用于描述类型擦除后的方法,“方法签名”还包含泛型类型信息 现在
javap
打印出方法签名(其中包含更多信息),当设置了-v
标志时,它还打印描述符
这表明javac
生成的枚举类的构造函数也有一个方法描述符,其参数类型为String
和int
。
现在也很清楚为什么Elipse和javac生成的代码都能工作。两者都使用参数String
和int
调用私有构造函数
还有什么需要解释的:为什么javac
创建了一个与描述符完全不同的签名-没有涉及泛型
无论如何,javac
关于enum构造函数的行为已经导致,javac
的错误报告是:
枚举声明的构造函数不需要有
用于存储方法签名的签名属性(如果1)构造函数
不是泛型的,2)其形式参数类型也不是泛型的
参数化类型或类型变量。如果javac希望
上面编写的构造函数的签名属性
下面的评论和案例的分类表明这是
javac
中的一个真正的错误,猜测一下,-g
标志的设置(控制调试信息的生成)。@CPerkins很好的假设,但我比较了-g
(生成所有调试信息)和-g:none
(不生成调试信息)并且似乎没有什么区别。您是否尝试了-v
(verbose)标志?它至少应该向您显示构造函数的描述符。我猜javac
标记了第一个参数强制执行的,这可能会使javap
忽略它们。@Clashsoft有趣。“描述符”显示参数(描述符:(Ljava/lang/String;I)V
),但我仍然不明白是什么导致了javac和Eclipse枚举之间的差异。正如我所说,javap
可能忽略了强制的
参数。我想Eclipse不会生成修饰符,而javac
会生成修饰符。谢谢,这解释了差异。事实证明,javac确实包含显式定义签名中的d enum构造函数参数,而不是常量名和序号的两个隐式参数。虽然我不认为这是javac中的一个错误,但更多的是VM规范中的一个缺陷。VM规范的签名属性规则,例如它应该出现的时间和应该包含的内容,似乎定义模糊。内部类的构造函数也存在类似的问题,它有一个隐藏参数,用于将引用传递给外部类。例如:class outer{class inner{inner(){}}
–构造函数在反汇编中显示为:outer$inner(outer);
。但是如果构造函数是泛型的:class outer{class Inner{Inner(){}}
,它在类文件中获得一个签名属性,javap将其反汇编为Outer$Inner();
–参数突然不可见,这在javac和ECJ中都是如此。奇怪的东西!至少-v
选项揭示了真相!
final class Foo extends java.lang.Enum<Foo> {
public static final Foo X;
private static final Foo[] ENUM$VALUES;
static {};
Code:
0: new #1 // class Foo
3: dup
4: ldc #12 // String X
6: iconst_0
7: invokespecial #13 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #17 // Field X:LFoo;
13: iconst_1
14: anewarray #1 // class Foo
17: dup
18: iconst_0
19: getstatic #17 // Field X:LFoo;
22: aastore
23: putstatic #19 // Field ENUM$VALUES:[LFoo;
26: return
private Foo(java.lang.String, int); // <--- here
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #23 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: return
public static Foo[] values();
Code:
0: getstatic #19 // Field ENUM$VALUES:[LFoo;
3: dup
4: astore_0
5: iconst_0
6: aload_0
7: arraylength
8: dup
9: istore_1
10: anewarray #1 // class Foo
13: dup
14: astore_2
15: iconst_0
16: iload_1
17: invokestatic #27 // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
20: aload_2
21: areturn
public static Foo valueOf(java.lang.String);
Code:
0: ldc #1 // class Foo
2: aload_0
3: invokestatic #35 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class Foo
9: areturn
}