为什么Java接口用javac -g编译的类文件中没有LocalVariableTable?

发布于 2025-01-14 20:30:13 字数 3469 浏览 3 评论 0原文

当Java文件是一个接口时,例如TestInterface.java

public interface TestInterface {

    void method(String parameterName);

}

我用javac -g TestInterface.java编译,然后用javap -v TestInterface反汇编code>,输出如下:

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文件是类时,如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

JDK版本:1.8.0_311

When the Java file is a interface, such as TestInterface.java:

public interface TestInterface {

    void method(String parameterName);

}

I compile with javac -g TestInterface.java and then disassemble with javap -v TestInterface, the output is as follows:

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"

And when the Java file is a class, such as TestClass.java:

public class TestClass {

    public void method(String parameterName) {

    }

}

Compile with javac -g TestClass.java and then disassemble with javap -v TestClass, the output is as follows:

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"

Why is LocalVariableTable not included in the class file of the interface?

JDK version: 1.8.0_311

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

戈亓 2025-01-21 20:30:13

因为这不是类文件结构的工作原理。

根据 JVM 规范,LocalVariableTableCode 属性的一部分:

LocalVariableTable 属性是 Code 属性 (§4.7.3) 的 attributes 表中的可选可变长度属性。调试器可以使用它来确定方法执行期间给定局部变量的值。

但是,接口方法是抽象的(即具有标志ACC_ABSTRACT),因此它们首先不能具有Code属性(§4.7.3)。

Code 属性是 method_info 结构 (§4.6) 的 attributes 表中的可变长度属性。 Code 属性包含 Java 虚拟机指令和方法的辅助信息 [...]

如果该方法是nativeabstract,并且不是类或接口初始化方法,则其method_info结构不得具有attributes 表中的 Code 属性。

因此,javac 不可能为您的抽象接口方法生成 LocalVariableTable,即使它愿意,因为这不会生成正确的类文件。

另一种思考方式是,在具体实现该方法之前,该方法的参数实际上并不“存在”。毕竟,抽象方法应该只是一个“需求”而没有“实现”。我真的不明白为什么你会期望有局部变量,因为它们是实现细节。

Because that's not how the class file structure works.

According to the JVM spec, the LocalVariableTable is part of a Code attribute:

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.

However, interface methods are abstract (i.e. having the flag ACC_ABSTRACT), so they can't have Code attributes in the first place (§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.

So it is not possible for javac to generate a LocalVariableTable for you abstract interface method, even if it wanted to, because that would not produce a correct class file.

Another way to think about it is that the parameter of the method doesn't actually "exist" until you concretely implement it. After all, an abstract method is supposed to just be a "requirement" without "implementations". I don't really see why you would expect there to be local variables, since they are implementation details.

铜锣湾横着走 2025-01-21 20:30:13

LocalVariableTable 描述了变量的范围——可以访问变量的字节码部分。

在类中,非抽象方法的参数有一个范围 - 它可以在整个方法体中访问。即使该方法为“空”,字节码仍然由单个 return 命令组成。

(实例方法中还有隐式 this 变量,该变量也可以在方法的整个主体中访问)。

在抽象接口方法(即不是默认方法或静态方法)中,没有可以访问该变量的字节码,因为该方法没有主体,因此没有字节码。因此,不需要 LocalVariableTable

抽象方法(无论是在接口还是类中)只是一个规范,它表示“实现者需要提供这个”。没有实施,没有主体等。

The LocalVariableTable describes the scope of the variable - the portion of the bytecode in which the variable is accessible.

In a class, the parameter of a non-abstract method has a scope - it's accessible in the whole of the body of the method. Even when the method is "empty", the bytecode still consists of a single return command.

(There's also the implicit this variable in an instance method, which is also accessible in the whole body of the method).

In an abstract interface method (i.e. not a default or static method), there's no bytecode in which that variable is accessible, because the method has no body, and thus no bytecode. Hence, there's no need for the LocalVariableTable.

An abstract method (whether in an interface or a class) is just a specification which says "implementors need to provide this". There is no implementation, no body etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文