java.lang.Object怎么可能在Java中实现?
How is it possible that java.lang.Object is implemented in Java?
根据 Java Language Specification, java.lang.Object
is the root of Java's inheritance hierarchy. Unlike C++ or Objective-C, programmers cannot specify their own root superclasses. Because of this, I figured it was impossible to actually define java.lang.Object
in Java itself. To my surprise, I found that OpenJDK indeed has a concrete implementation of java.lang.Object
.
我想看看是否可以用 JDK/JRE 1.8 编译和 运行 我自己的 java.lang.Object
版本,所以我写成 Object.java
:
package java.lang;
public class Object {
public static void main(String[] args) {
System.out.println("Hello world from custom java.lang.Object!");
}
}
它用 javac
编译得很好,但如果我尝试通过 java -cp . java.lang.Object
执行类文件,JVM 会出错并显示此消息:
Error: Main method not found in class java.lang.Object, please define the main method as:
public static void main(String[] args)
这表明 JVM 在类路径中使用的是库存 java.lang.Object
而不是 Object.class
。
是否可以将 Java 中的 java.lang.Object
定义为最终用户?幕后发生了什么?我看到两种可能性:
- JVM 加载一个真正的
Object.class
文件,该文件是从 OpenJDK 的 Object.java
编译而来的。如果是这种情况,编译器是否做了一些特殊的事情来防止类文件将自己指定为超类?
java.lang.Object
的实现是 JVM 固有的。如果是这样,为什么OpenJDK中会有一个Object.java
?
这是一个非常酷的实验。
但这就是 Java 的工作方式
- 由于 Java 中的每个 class 都必须扩展
java.lang.Object
,您的自定义 Object
class 也会扩展它。
- 要加载任何 class,Java 需要加载它的父 class。因此,当 Java 尝试 运行 自定义
Object
class 中的 main()
方法时,它会加载真正的 java.lang.Object
class。
- 一旦真正的
java.lang.Object
class 被加载,JVM 就会尝试 运行 class 的 main()
方法。由于它不存在,您的应用程序失败。
你可以修改java.lang.Object
(例如通过添加public static void main()
方法),但是为了被JVM加载和使用,修改后的class 需要添加到 bootstrap class 路径。
在 JDK 8 这可以通过
完成
java -Xbootclasspath/p:<path>
在 JDK 9+ 这需要修补 java.base
模块:
java --patch-module java.base=<path>
当 JVM 启动时,它通过 bootstrap class 加载器加载 java.lang.Object
就像任何其他 class 一样,所以 java.lang.Object
添加了 main
方法可以实际执行:
$ java -Xbootclasspath/p:. java.lang.Object
Hello world from custom java.lang.Object!
但是,如果您尝试删除现有的 java.lang.Object
方法、添加新的虚拟方法、添加字段或以其他方式更改现有布局 - 这将不起作用。最有可能的是,JVM 将因致命错误而崩溃。
这是因为 JVM 期望 java.lang.Object
具有已知布局。 JVM源代码中有硬编码的偏移量,对现有方法的引用等。对于其他内部classes也是如此,如java.lang.String
、java.lang.Class
、java.lang.ref.Reference
和类似的。
关于 Object 的 superclass,JVM Specification 中明确描述了一个例外:
If the value of the super_class item is zero, then this class file
must represent the class Object, the only class or interface without a
direct superclass.
Java 编译器和 JVM 都知道此异常,并在 compiling Object.java
and when loading Object.class
.
时执行此规则
您可以在 Java 中实现 java.lang.Object
并且您正在使用的实际 class 确实是从 [=82] 附带的 Object.java
文件创建的=].
Java® 语言规范在 Chapter 8. Classes 中说:
Each class except Object
is an extension of (that is, a subclass of) a single existing class (§8.1.4) and may implement interfaces (§8.1.5).
因此 Object
的超类型的缺失在语言中是固定的。
您可以使用您实验的源代码并尝试添加 extends
或 implements
子句,看看编译器是否会拒绝它。
当您编译 class java.lang.Object
时,生成的 class 文件将是唯一没有超类型的文件。请参阅 Java® 虚拟机规范,§4.1., The ClassFile Structure:
- super_class
-
For a class, the value of the super_class
item either must be zero or must be a valid index into the constant_pool
table. If the value of the super_class
item is nonzero, the constant_pool
entry at that index must be a CONSTANT_Class_info
structure representing the direct superclass of the class defined by this class
file. Neither the direct superclass nor any of its superclasses may have the ACC_FINAL
flag set in the access_flags
item of its ClassFile
structure.
If the value of the super_class
item is zero, then this class
file must represent the class Object
, the only class or interface without a direct superclass.
For an interface, the value of the super_class
item must always be a valid index into the constant_pool
table. The constant_pool
entry at that index must be a CONSTANT_Class_info
structure representing the class Object
.
因此,即使接口在 class 文件(指向 Object
)中也有 superclass 的条目和 java.lang.Object
的 class 文件超级class.
唯一一个零条目
当您尝试在运行时加载您的 Object
class 版本时,您偶然发现无法加载 [=37= 的 classes ] 包(或任何限定名称以 java.
开头的 class)通常通过 class 路径。
在 Java9 之前,您必须设置 bootstrap class path to include your version. Starting with Java 9, the class java.lang.Object
must belong to the java.base
module, which is loaded in an implementation specific manner. You’d have to use the --patch-module
选项来注入您自己的版本。
但是你必须小心你写入自己版本的内容。其他 classes 和环境有很多期望,不满足这些期望可能会(严重)破坏它。
JLS, §4.3.2. The Class Object 列出了预期的方法和指向为其中一些方法定义特殊语言语义的其他章节的链接。
根据 Java Language Specification, java.lang.Object
is the root of Java's inheritance hierarchy. Unlike C++ or Objective-C, programmers cannot specify their own root superclasses. Because of this, I figured it was impossible to actually define java.lang.Object
in Java itself. To my surprise, I found that OpenJDK indeed has a concrete implementation of java.lang.Object
.
我想看看是否可以用 JDK/JRE 1.8 编译和 运行 我自己的 java.lang.Object
版本,所以我写成 Object.java
:
package java.lang;
public class Object {
public static void main(String[] args) {
System.out.println("Hello world from custom java.lang.Object!");
}
}
它用 javac
编译得很好,但如果我尝试通过 java -cp . java.lang.Object
执行类文件,JVM 会出错并显示此消息:
Error: Main method not found in class java.lang.Object, please define the main method as:
public static void main(String[] args)
这表明 JVM 在类路径中使用的是库存 java.lang.Object
而不是 Object.class
。
是否可以将 Java 中的 java.lang.Object
定义为最终用户?幕后发生了什么?我看到两种可能性:
- JVM 加载一个真正的
Object.class
文件,该文件是从 OpenJDK 的Object.java
编译而来的。如果是这种情况,编译器是否做了一些特殊的事情来防止类文件将自己指定为超类? java.lang.Object
的实现是 JVM 固有的。如果是这样,为什么OpenJDK中会有一个Object.java
?
这是一个非常酷的实验。 但这就是 Java 的工作方式
- 由于 Java 中的每个 class 都必须扩展
java.lang.Object
,您的自定义Object
class 也会扩展它。 - 要加载任何 class,Java 需要加载它的父 class。因此,当 Java 尝试 运行 自定义
Object
class 中的main()
方法时,它会加载真正的java.lang.Object
class。 - 一旦真正的
java.lang.Object
class 被加载,JVM 就会尝试 运行 class 的main()
方法。由于它不存在,您的应用程序失败。
你可以修改java.lang.Object
(例如通过添加public static void main()
方法),但是为了被JVM加载和使用,修改后的class 需要添加到 bootstrap class 路径。
在 JDK 8 这可以通过
完成java -Xbootclasspath/p:<path>
在 JDK 9+ 这需要修补 java.base
模块:
java --patch-module java.base=<path>
当 JVM 启动时,它通过 bootstrap class 加载器加载 java.lang.Object
就像任何其他 class 一样,所以 java.lang.Object
添加了 main
方法可以实际执行:
$ java -Xbootclasspath/p:. java.lang.Object
Hello world from custom java.lang.Object!
但是,如果您尝试删除现有的 java.lang.Object
方法、添加新的虚拟方法、添加字段或以其他方式更改现有布局 - 这将不起作用。最有可能的是,JVM 将因致命错误而崩溃。
这是因为 JVM 期望 java.lang.Object
具有已知布局。 JVM源代码中有硬编码的偏移量,对现有方法的引用等。对于其他内部classes也是如此,如java.lang.String
、java.lang.Class
、java.lang.ref.Reference
和类似的。
关于 Object 的 superclass,JVM Specification 中明确描述了一个例外:
If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.
Java 编译器和 JVM 都知道此异常,并在 compiling Object.java
and when loading Object.class
.
您可以在 Java 中实现 java.lang.Object
并且您正在使用的实际 class 确实是从 [=82] 附带的 Object.java
文件创建的=].
Java® 语言规范在 Chapter 8. Classes 中说:
Each class except
Object
is an extension of (that is, a subclass of) a single existing class (§8.1.4) and may implement interfaces (§8.1.5).
因此 Object
的超类型的缺失在语言中是固定的。
您可以使用您实验的源代码并尝试添加 extends
或 implements
子句,看看编译器是否会拒绝它。
当您编译 class java.lang.Object
时,生成的 class 文件将是唯一没有超类型的文件。请参阅 Java® 虚拟机规范,§4.1., The ClassFile Structure:
- super_class
For a class, the value of the
super_class
item either must be zero or must be a valid index into theconstant_pool
table. If the value of thesuper_class
item is nonzero, theconstant_pool
entry at that index must be aCONSTANT_Class_info
structure representing the direct superclass of the class defined by thisclass
file. Neither the direct superclass nor any of its superclasses may have theACC_FINAL
flag set in theaccess_flags
item of itsClassFile
structure.If the value of the
super_class
item is zero, then thisclass
file must represent the classObject
, the only class or interface without a direct superclass.For an interface, the value of the
super_class
item must always be a valid index into theconstant_pool
table. Theconstant_pool
entry at that index must be aCONSTANT_Class_info
structure representing the classObject
.
因此,即使接口在 class 文件(指向 Object
)中也有 superclass 的条目和 java.lang.Object
的 class 文件超级class.
当您尝试在运行时加载您的 Object
class 版本时,您偶然发现无法加载 [=37= 的 classes ] 包(或任何限定名称以 java.
开头的 class)通常通过 class 路径。
在 Java9 之前,您必须设置 bootstrap class path to include your version. Starting with Java 9, the class java.lang.Object
must belong to the java.base
module, which is loaded in an implementation specific manner. You’d have to use the --patch-module
选项来注入您自己的版本。
但是你必须小心你写入自己版本的内容。其他 classes 和环境有很多期望,不满足这些期望可能会(严重)破坏它。
JLS, §4.3.2. The Class Object 列出了预期的方法和指向为其中一些方法定义特殊语言语义的其他章节的链接。