为什么Java接口javac -g编译的class文件中没有LocalVariableTable?
Why is there no LocalVariableTable in the class file compiled with javac -g for the Java interface?
当Java文件为接口时,如TestInterface.java
:
public interface TestInterface {
void method(String parameterName);
}
我用javac -g TestInterface.java
编译,然后用javap -v TestInterface
反汇编,输出结果如下:
Classfile /private/tmp/TestInterface.class
Last modified Mar 17, 2022; size 148 bytes
MD5 checksum da2f58afc0eaf77badc94c90de385198
Compiled from "TestInterface.java"
public interface TestInterface
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
#1 = Class #7 // TestInterface
#2 = Class #8 // java/lang/Object
#3 = Utf8 method
#4 = Utf8 (Ljava/lang/String;)V
#5 = Utf8 SourceFile
#6 = Utf8 TestInterface.java
#7 = Utf8 TestInterface
#8 = Utf8 java/lang/Object
{
public abstract void method(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "TestInterface.java"
而当Java文件为class时,如TestClass.java
:
public class TestClass {
public void method(String parameterName) {
}
}
用javac -g TestClass.java
编译,然后用javap -v TestClass
反汇编,输出结果如下:
Classfile /private/tmp/TestClass.class
Last modified Mar 17, 2022; size 389 bytes
MD5 checksum 8e124ecce6632ad6e1a5bb45888a3168
Compiled from "TestClass.java"
public class TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#17 // java/lang/Object."<init>":()V
#2 = Class #18 // TestClass
#3 = Class #19 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 LTestClass;
#11 = Utf8 method
#12 = Utf8 (Ljava/lang/String;)V
#13 = Utf8 parameterName
#14 = Utf8 Ljava/lang/String;
#15 = Utf8 SourceFile
#16 = Utf8 TestClass.java
#17 = NameAndType #4:#5 // "<init>":()V
#18 = Utf8 TestClass
#19 = Utf8 java/lang/Object
{
public TestClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestClass;
public void method(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LTestClass;
0 1 1 parameterName Ljava/lang/String;
}
SourceFile: "TestClass.java"
为什么LocalVariableTable
没有包含在接口的class文件中?
JDK版本:1.8.0_311
LocalVariableTable
描述了变量的范围 - 字节码中可访问变量的部分。
在 class 中,non-abstract 方法的参数有一个范围 - 它可以在整个方法主体中访问。即使方法为“空”,字节码仍然由单个 return
命令组成。
(实例方法中还有隐式的this
变量,在整个方法体中也可以访问)
在抽象接口方法(即不是 default
或 static
方法)中,没有可访问该变量的字节码,因为该方法没有主体,因此没有字节码。因此,不需要 LocalVariableTable
.
抽象方法(无论是在接口中还是在 class 中)只是一个说明“实现者需要提供这个”的规范。没有实现,没有正文等
因为那不是 class 文件结构的工作方式。
根据 JVM spec,LocalVariableTable
是 Code
属性的一部分:
The LocalVariableTable
attribute is an optional variable-length attribute in the attributes
table of a Code
attribute (§4.7.3). It may be used by debuggers to determine the value of a given local variable during the execution of a method.
但是,接口方法是抽象的(即具有标志 ACC_ABSTRACT
),因此它们首先不能具有 Code
属性 (§4.7.3)。
The Code
attribute is a variable-length attribute in the attributes
table of a method_info
structure (§4.6). A Code
attribute contains the Java Virtual Machine instructions and auxiliary information for a method [...]
If the method is either native
or abstract
, and is not a class or interface initialization method, then its method_info
structure must not have a Code
attribute in its attributes
table.
因此 javac
不可能为您生成一个 LocalVariableTable
抽象接口方法,即使它想要,因为那样不会生成正确的 class 文件。
另一种思考方法是,在您具体实现之前,方法的参数实际上并不“存在”。毕竟,抽象方法应该只是一个没有“实现”的“要求”。我真的不明白为什么你会期望有局部变量,因为它们是实现细节。
当Java文件为接口时,如TestInterface.java
:
public interface TestInterface {
void method(String parameterName);
}
我用javac -g TestInterface.java
编译,然后用javap -v TestInterface
反汇编,输出结果如下:
Classfile /private/tmp/TestInterface.class
Last modified Mar 17, 2022; size 148 bytes
MD5 checksum da2f58afc0eaf77badc94c90de385198
Compiled from "TestInterface.java"
public interface TestInterface
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
#1 = Class #7 // TestInterface
#2 = Class #8 // java/lang/Object
#3 = Utf8 method
#4 = Utf8 (Ljava/lang/String;)V
#5 = Utf8 SourceFile
#6 = Utf8 TestInterface.java
#7 = Utf8 TestInterface
#8 = Utf8 java/lang/Object
{
public abstract void method(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "TestInterface.java"
而当Java文件为class时,如TestClass.java
:
public class TestClass {
public void method(String parameterName) {
}
}
用javac -g TestClass.java
编译,然后用javap -v TestClass
反汇编,输出结果如下:
Classfile /private/tmp/TestClass.class
Last modified Mar 17, 2022; size 389 bytes
MD5 checksum 8e124ecce6632ad6e1a5bb45888a3168
Compiled from "TestClass.java"
public class TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#17 // java/lang/Object."<init>":()V
#2 = Class #18 // TestClass
#3 = Class #19 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 LTestClass;
#11 = Utf8 method
#12 = Utf8 (Ljava/lang/String;)V
#13 = Utf8 parameterName
#14 = Utf8 Ljava/lang/String;
#15 = Utf8 SourceFile
#16 = Utf8 TestClass.java
#17 = NameAndType #4:#5 // "<init>":()V
#18 = Utf8 TestClass
#19 = Utf8 java/lang/Object
{
public TestClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestClass;
public void method(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LTestClass;
0 1 1 parameterName Ljava/lang/String;
}
SourceFile: "TestClass.java"
为什么LocalVariableTable
没有包含在接口的class文件中?
JDK版本:1.8.0_311
LocalVariableTable
描述了变量的范围 - 字节码中可访问变量的部分。
在 class 中,non-abstract 方法的参数有一个范围 - 它可以在整个方法主体中访问。即使方法为“空”,字节码仍然由单个 return
命令组成。
(实例方法中还有隐式的this
变量,在整个方法体中也可以访问)
在抽象接口方法(即不是 default
或 static
方法)中,没有可访问该变量的字节码,因为该方法没有主体,因此没有字节码。因此,不需要 LocalVariableTable
.
抽象方法(无论是在接口中还是在 class 中)只是一个说明“实现者需要提供这个”的规范。没有实现,没有正文等
因为那不是 class 文件结构的工作方式。
根据 JVM spec,LocalVariableTable
是 Code
属性的一部分:
The
LocalVariableTable
attribute is an optional variable-length attribute in theattributes
table of aCode
attribute (§4.7.3). It may be used by debuggers to determine the value of a given local variable during the execution of a method.
但是,接口方法是抽象的(即具有标志 ACC_ABSTRACT
),因此它们首先不能具有 Code
属性 (§4.7.3)。
The
Code
attribute is a variable-length attribute in theattributes
table of amethod_info
structure (§4.6). ACode
attribute contains the Java Virtual Machine instructions and auxiliary information for a method [...]If the method is either
native
orabstract
, and is not a class or interface initialization method, then itsmethod_info
structure must not have aCode
attribute in itsattributes
table.
因此 javac
不可能为您生成一个 LocalVariableTable
抽象接口方法,即使它想要,因为那样不会生成正确的 class 文件。
另一种思考方法是,在您具体实现之前,方法的参数实际上并不“存在”。毕竟,抽象方法应该只是一个没有“实现”的“要求”。我真的不明白为什么你会期望有局部变量,因为它们是实现细节。