Hvordan er det muligt, at java.lang.Object er implementeret i Java?
Du kan ændre java.lang.Object
(f.eks. ved at tilføje public static void main()
metode), men for at blive indlæst og brugt af JVM, skal den modificerede klasse tilføjes til bootstrap-klassestien.
På JDK 8 kan dette gøres med
java -Xbootclasspath/p:<path>
På JDK 9+ kræver dette patching java.base
modul:
java --patch-module java.base=<path>
Når JVM starter, indlæser den java.lang.Object
af bootstrap-klasseindlæseren ligesom enhver anden klasse, så java.lang.Object
med den tilføjede main
metode kan faktisk udføres:
$ java -Xbootclasspath/p:. java.lang.Object
Hello world from custom java.lang.Object!
Men hvis du prøver at fjerne eksisterende java.lang.Object
metoder, tilføje nye virtuelle metoder, tilføje felter eller på anden måde ændre det eksisterende layout - dette vil ikke virke. Mest sandsynligt vil JVM'en bare gå ned med den fatale fejl.
Dette skyldes, at JVM forventer java.lang.Object
at have det kendte layout. Der er hårdkodede forskydninger i JVM-kildekoden, referencer til de afsluttende metoder osv. Det samme gælder for andre iboende klasser som java.lang.String
, java.lang.Class
, java.lang.ref.Reference
og lignende.
Hvad angår Objects superklasse, er der en undtagelse, der udtrykkeligt er beskrevet i JVM Specification:
Hvis værdien af super_class elementet er nul, så skal denne klassefil repræsentere klassen Object, den eneste klasse eller grænseflade uden en direkte superklasse.
Både Java-kompileren og JVM kender til denne undtagelse og håndhæver denne regel ved kompilering af Object.java
og ved indlæsning af Object.class
.
Du kan implementere java.lang.Object
i Java, og den faktiske klasse, du bruger, er faktisk blevet oprettet fra Object.java
fil, der leveres med JDK.
Java®-sprogspecifikationen siger i kapitel 8. Klasser:
Hver klasse undtagen Object
er en udvidelse af (det vil sige en underklasse af) en enkelt eksisterende klasse (§8.1.4) og kan implementere grænseflader (§8.1.5).
Altså fraværet af supertyper for Object
er fastsat i sproget.
Du kan bruge kildekoden til dit eksperiment og prøve at tilføje en extends
eller implements
klausul og se, at compileren vil afvise det.
Når du kompilerer klassen java.lang.Object
, vil den resulterende klassefil være den eneste, der ikke har nogen supertype. Se Java® Virtual Machine Specification, §4.1., ClassFile Structure:
- superklasse
-
For en klasse, værdien af
super_class
elementet skal enten være nul eller skal være et gyldigt indeks iconstant_pool
bord. Hvis værdien af super_class
elementet er ikke-nul,constant_pool
indgang i det indeks skal være enCONSTANT_Class_info
struktur, der repræsenterer den direkte superklasse af klassen defineret af denneclass
fil. Hverken den direkte superklasse eller nogen af dens superklasser må haveACC_FINAL
flag sat iaccess_flags
element i densClassFile
struktur.Hvis værdien af
super_class
elementet er nul, så denneclass
fil skal repræsentere klassenObject
, den eneste klasse eller grænseflade uden en direkte superklasse.For en grænseflade, værdien af
super_class
element skal altid være et gyldigt indeks iconstant_pool
bord.constant_pool
indgang i det indeks skal være enCONSTANT_Class_info
struktur, der repræsenterer klassenObject
.
Så selv grænseflader har en indgang for superklassen i klassefilen (der peger på Object
) og klassefilen for java.lang.Object
er den eneste med nul tilmelding til superklassen.
Når du prøver at indlæse din version af Object
klasse under runtime, falder du over det faktum, at du ikke kan indlæse klasser af java.lang
pakke (eller enhver klasse, hvis kvalificerede navn starter med java.
) gennem klassestien generelt.
Før Java 9 skulle du konfigurere bootstrap-klassenstien for at inkludere din version. Startende med Java 9, klassen java.lang.Object
skal tilhøre java.base
modul, som indlæses på en implementeringsspecifik måde. Du skal bruge --patch-module
mulighed for at injicere din egen version.
Men du skal passe på med, hvad du skriver ind i din egen version. Der er mange forventninger fra andre klasser og miljøet, og hvis de ikke opfyldes, kan det bryde (dårligt).
JLS, §4.3.2. Klasseobjektet viser de forventede metoder og links til andre kapitler, der definerer speciel sprogsemantik for nogle af dem.
Det er et rigtig fedt eksperiment. Men det er sådan, Java fungerer
- Da hver klasse i Java skal udvide
java.lang.Object
, din tilpassedeObject
klasse udvider også det. - For at indlæse en klasse skal Java indlæse dens overordnede klasser. Så når Java forsøger at køre
main()
metode i din brugerdefineredeObject
klasse, indlæser den den rigtigejava.lang.Object
klasse. - Så snart ægte
java.lang.Object
klasse er indlæst, prøver JVM at køremain()
metode i den klasse. Da den ikke eksisterer, fejler din applikation med.