Java如何强制类扩展对象?

Java如何强制类扩展对象?,java,object,inheritance,java-native-interface,bytecode,Java,Object,Inheritance,Java Native Interface,Bytecode,这是如何在内部发生的?使用什么机制?JNI,反射还是其他什么?编译器是否包含扩展子句,可能?不完全正确 如果所有类还没有扩展另一个类,它们都会扩展Object,除了一个例外:java.lang.Object本身不扩展任何类 如果超类java.lang.Object的代码没有指定另一个超类,则编译器会在类文件中嵌入该超类 你甚至可以自己看到它,如果你用你最喜欢的编辑器打开.class文件,你可以看到嵌入在二进制数据中的字符串java/lang/Object (您可以编译一个非常简单的源文件,如pu

这是如何在内部发生的?使用什么机制?JNI,反射还是其他什么?编译器是否包含
扩展
子句,可能?

不完全正确

如果所有类还没有扩展另一个类,它们都会扩展Object,除了一个例外:
java.lang.Object
本身不扩展任何类

如果超类
java.lang.Object
的代码没有指定另一个超类,则编译器会在类文件中嵌入该超类

你甚至可以自己看到它,如果你用你最喜欢的编辑器打开
.class
文件,你可以看到嵌入在二进制数据中的字符串
java/lang/Object


(您可以编译一个非常简单的源文件,如
public class a{}
,以最好地观察这一点)

这是一个关于java编译器内部的有趣问题。我发现了一个文档列表,提供了javac的高级视图。像OpenJdk这样的特定实现的源代码也可以在网上找到

编译包括3个高级步骤:

  • “解析并输入”
  • “批注处理”
  • “分析并生成”
  • 我编写了一些代码来使用编译器树API测试这些步骤,它提供了一个javac接口

    static class TestFileObject extends SimpleJavaFileObject {
        public TestFileObject() {
            super(URI.create("Test.java"), JavaFileObject.Kind.SOURCE);
        }
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return "class Test { private int x; }";
        }
    }
    
    public static void main(String[] args) throws IOException {
        JavacTool tool = JavacTool.create();
        JavacTask task = tool.getTask(null, null, null, null, null,
                com.sun.tools.javac.util.List.of(new TestFileObject()));
    
        // Step 1, Parse
        Iterator<? extends CompilationUnitTree> trees = task.parse().iterator();
    
        // Step 3, Analyze
        // Iterator<? extends Element> elements = task.analyze().iterator();
    
        // Step 3, Generate
        // Iterator<? extends JavaFileObject> files = task.generate().iterator();
    
        while(trees.hasNext()) {
            CompilationUnitTree cu = trees.next();
            System.out.println(cu.getTypeDecls());
        }
    }
    
    因此AST不包含对
    java.lang.Object
    的引用。接下来,我取消了“步骤3,分析”的注释,并使用结果输出重新运行代码:

    class Test {
        private int x;
    }
    
    class Test {
    
        Test() {
            super();
        }
        private int x;
    }
    
    请注意,此步骤中添加了
    Test()
    构造函数和
    super()
    , 这也对应于步骤3的说明:

    在分析树时,可以找到对成功编译所需的类的引用,但这些类没有明确指定用于编译

    最后,我取消了
    步骤3,Generate
    ,它创建了
    Test.class
    文件。调用
    javap-c Test.class
    ,得到的字节码是blelow:

    class Test {
      Test();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    }
    
    类测试{
    Test();
    代码:
    0:aload_0
    1:invokespecial#1//方法java/lang/Object。“:()V
    4:返回
    }
    

    因此,我的结论是字节码生成步骤添加了
    java.lang.Object
    相关逻辑。

    正确。如果您的类没有显式扩展另一个类,那么编译器将确保它扩展原始类
    对象
    。我认为这是一个很好的问题,假设它不是重复的。我不认为编译器在您的链接@ElliottFrisch中注入
    extends Object
    。给定一个(可能是通用的)类声明C(n≥ 0,C≠ 对象),类类型C的直接超类是C声明的extends子句中给出的类型(如果存在extends子句),或者是Object。当然,这一点即使是OP也广为人知。问题是发生这种情况的过程是什么(即编译器是在字节码中注入
    扩展对象
    ,还是以其他方式发生?)所有类都扩展Object。其中许多类不直接扩展Object。@chrylis不,它们不直接扩展Object。Object不扩展Object。我不知道如何在我的回答中更清楚地说明这一点?哦,所有类都继承java.lang.Object,但它们只扩展它们的直接超类。我要明确指出,类以某种方式继承了java.lang.Object,如果它是
    java.lang.Object
    以外的任何类。即使它直接扩展另一个类,一些基类也会扩展
    java.lang.Object
    。我不明白你为什么要谈论多级继承(堆栈上的这个对话已经存在)。但我得到了我的答案。谢谢@Erwin Bolwidt。如果没有人更详细地回答这个问题(我指的是javac是如何做到的),我就关闭它。为了完成这幅图,编译器将始终包含一个超类,而JVM的验证器将拒绝一个没有超类的类(除非是
    java/lang/Object
    类本身).不相关。超类出现在类文件头中,而不仅仅是字节码中。我的观点是添加java.lang.Object是编译管道的代码生成步骤,而不是解析器、注释或分析步骤。如果您不同意,请提供证据,以便我们可以从中学习。谢谢。