According to the 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
.
I wanted to see if it was possible to compile and run my own version of java.lang.Object
with JDK/JRE 1.8, so I wrote this as Object.java
:
package java.lang;
public class Object {
public static void main(String[] args) {
System.out.println("Hello world from custom java.lang.Object!");
}
}
It compiled just fine with javac
, but if I try to execute the classfile via java -cp . java.lang.Object
, the JVM errors out with this message:
Error: Main method not found in class java.lang.Object, please define the main method as:
public static void main(String[] args)
which suggests that the JVM is using the stock java.lang.Object
instead of the Object.class
in the classpath.
Is it possible to define java.lang.Object
in Java as an end user? What is going on under the hood? I see two possibilities:
Object.class
file that was compiled from the OpenJDK's Object.java
. If this is the case, does the compiler do something special to prevent the classfile from specifying itself as the superclass?java.lang.Object
's implementation is intrinsic to the JVM. If this is the case, why is there an Object.java
in OpenJDK?You can implement java.lang.Object
in Java and the actual class you’re using has been indeed created from the Object.java
file that ships with the JDK.
The Java® Language Specification says in 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).
So the absence of supertypes for Object
is fixed in the language.
You can use the source code of your experiment and try to add an extends
or implements
clause and see that the compiler will reject it.
When you compile the class java.lang.Object
, the resulting class file will be the only one that has no supertype. See The Java® Virtual Machine Specification, §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
.
So even interfaces have an entry for the superclass in the class file (pointing to Object
) and the class file for java.lang.Object
is the only one with a zero entry for the super class.
When you try to load your version of the Object
class at runtime, you stumble across the fact that you can’t load classes of the java.lang
package (or any class whose qualified name starts with java.
) through the class path in general.
Prior to Java 9, you would have to set up the 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
option to inject your own version.
But you have to be careful with what you write into your own version. There are a lot of expectations by other classes and the environment and not meeting them can break it (badly).
JLS, §4.3.2. The Class Object lists the expected methods and links to other chapters that define special language semantics for some of them.
You can modify java.lang.Object
(e.g. by adding public static void main()
method), but in order to be loaded and used by the JVM, the modified class needs to be added to the bootstrap class path.
On JDK 8 this can be done with
java -Xbootclasspath/p:<path>
On JDK 9+ this requires patching java.base
module:
java --patch-module java.base=<path>
When the JVM starts, it loads java.lang.Object
by the bootstrap class loader just like any other class, so java.lang.Object
with the added main
method can be actually executed:
$ java -Xbootclasspath/p:. java.lang.Object
Hello world from custom java.lang.Object!
However, if you try to remove existing java.lang.Object
methods, add new virtual methods, add fields or otherwise change the existing layout - this won't work. Most likely, the JVM will just crash with the fatal error.
This is because the JVM expects java.lang.Object
to have the known layout. There are hard-coded offsets in the JVM source code, references to the exiting methods, etc. The same is true for other intrinsic classes like java.lang.String
, java.lang.Class
, java.lang.ref.Reference
and similar.
As to Object's superclass, there is an exception explicitly described in 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.
Both Java compiler and the JVM know about this exception, and enforce this rule when compiling Object.java
and when loading Object.class
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With