AbschnitteBearbeiten
Die Struktur der Java-Klassendatei besteht aus 10 grundlegenden Abschnitten:
- Magic Number: 0xCAFEBABE
- Version des Klassendateiformats: die Minor- und Major-Version der Klassendatei
- Constant Pool: Pool von Konstanten für die Klasse
- Zugriffsflags: z.B. ob die Klasse abstrakt, statisch usw. ist
- Diese Klasse: Der Name der aktuellen Klasse
- Superklasse: Der Name der Superklasse
- Interfaces: Alle Schnittstellen in der Klasse
- Felder: Alle Felder in der Klasse
- Methoden: Beliebige Methoden in der Klasse
- Attribute: Alle Attribute der Klasse (z. B. der Name der Quelldatei usw.)
Magic NumberEdit
Klassendateien werden durch den folgenden 4-Byte-Header (in Hexadezimal) gekennzeichnet: CA FE BA BE
(die ersten 4 Einträge in der folgenden Tabelle). Die Geschichte dieser magischen Zahl wurde von James Gosling erklärt, der sich auf ein Restaurant in Palo Alto bezog:
„Wir gingen früher zum Mittagessen in ein Lokal namens St. Michael’s Alley. Laut einer lokalen Legende traten dort in der dunklen Vergangenheit die Grateful Dead auf, bevor sie groß rauskamen. Es war ein ziemlich abgefahrener Ort, der definitiv eine Art Grateful Dead Place war. Als Jerry starb, errichteten sie sogar einen kleinen buddhistischen Schrein. Wenn wir dort hingingen, nannten wir den Ort Cafe Dead. Irgendwann ist mir aufgefallen, dass das eine HEX-Zahl ist. Ich war gerade dabei, den Code für ein Dateiformat zu überarbeiten und brauchte ein paar magische Zahlen: eine für die persistente Objektdatei und eine für Klassen. Ich benutzte CAFEDEAD für das Objektdateiformat, und bei der Suche nach 4-Zeichen-Hex-Wörtern, die nach „CAFE“ passten (das schien ein gutes Thema zu sein), stieß ich auf BABE und beschloss, es zu benutzen, was zu diesem Zeitpunkt weder besonders wichtig noch dazu bestimmt schien, irgendwo anders als im Mülleimer der Geschichte zu landen. So wurde CAFEBABE das Klassendateiformat und CAFEDEAD das Format für persistente Objekte. Aber die Möglichkeit, persistente Objekte zu erstellen, wurde abgeschafft, und damit auch die Verwendung von CAFEDEAD – es wurde schließlich durch RMI ersetzt.
Allgemeines LayoutEdit
Da die Klassendatei Elemente mit variabler Größe und keine eingebetteten Datei-Offsets (oder Zeiger) enthält, wird sie in der Regel sequentiell geparst, vom ersten Byte bis zum Ende. Auf der untersten Ebene wird das Dateiformat anhand einiger grundlegender Datentypen beschrieben:
- u1: eine vorzeichenlose 8-Bit-Ganzzahl
- u2: eine vorzeichenlose 16-Bit-Ganzzahl in Big-Endian-Byte-Reihenfolge
- u4: eine vorzeichenlose 32-Bit-Ganzzahl in Big-Endian-Byte-Reihenfolge
- table: ein Array von Elementen variabler Länge eines bestimmten Typs. Die Anzahl der Elemente in der Tabelle wird durch eine vorangestellte Zählnummer identifiziert (die Zählnummer ist ein u2), aber die Größe der Tabelle in Bytes kann nur durch die Untersuchung der einzelnen Elemente bestimmt werden.
Einige dieser Grundtypen werden dann je nach Kontext als Werte höherer Ebene (wie Zeichenketten oder Gleitkommazahlen) neu interpretiert.es gibt keine Erzwingung der Wortausrichtung, und daher werden keine Auffüllungsbytes verwendet.das Gesamtlayout der Klassendatei ist in der folgenden Tabelle dargestellt.
Byte Offset | Größe | Typ oder Wert | Beschreibung |
---|---|---|---|
0 | 4 Bytes | u1 = 0xCA hex |
magische Zahl (CAFEBABE), die verwendet wird, um die Datei als konform mit dem Klassendateiformat zu identifizieren |
1 | u1 = 0xFE hex |
||
2 | u1 = 0xBA hex |
||
3 | u1 = 0xBE hex |
||
4 | 2 Bytes | u2 | kleine Versionsnummer des |
5 | |||
6 | 2 Bytes | u2 | Hauptversionsnummer des verwendeten Klassendateiformats.
Java SE 17 = 61 (0x3D hex), |
7 | |||
8 | 2 Bytes | u2 | constant pool count, Anzahl der Einträge in der folgenden Konstantenpooltabelle. Diese Anzahl ist mindestens um eins größer als die tatsächliche Anzahl der Einträge; siehe folgende Diskussion. |
9 | |||
10 | cpsize (variabel) | table | constant pool table, ein Array von Konstanten-Pool-Einträgen variabler Größe, die Elemente wie Literalzahlen, Strings und Referenzen auf Klassen oder Methoden enthalten. Indiziert ab 1, enthält (Konstanten-Pool-Anzahl – 1) Anzahl der Einträge insgesamt (siehe Hinweis). |
… | |||
… | |||
… | |||
10+cpsize | 2 bytes | u2 | Zugriffsflags, eine Bitmaske |
11+cpsize | |||
12+cpsize | 2 Bytes | u2 | identifiziert diese Klasse, Index in den Konstanten-Pool zu einem „Class“-Eintrag |
13+cpsize | |||
14+cpsize | 2 Bytes | u2 | identifiziert Superklasse, Index in den Konstanten-Pool zu einem Eintrag vom Typ „Klasse“ |
15+cpsize | |||
16+cpsize | 2 Bytes | u2 | Schnittstellenanzahl, Anzahl der Einträge in der folgenden Schnittstellentabelle |
17+cpsize | |||
18+cpsize | isize (variabel) | table | interface table: ein Array mit variabler Länge von konstanten Pool-Indizes, die die von dieser Klasse implementierten Schnittstellen beschreiben |
… | |||
… | |||
… | |||
18+cpsize+isize | 2 bytes | u2 | Feldanzahl, Anzahl der Einträge in der folgenden Feldtabelle |
19+cpsize+isize | |||
20+cpsize+isize | fsize (variable) | Tabelle | Feldtabelle, Array variabler Länge von Feldern
jedes Element ist eine field_info-Struktur, definiert in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5 |
… | |||
… | |||
… | |||
20+cpsize+isize+fsize | 2 bytes | u2 | method count, Anzahl der Einträge in der folgenden Methodentabelle |
21+cpsize+isize+fsize | |||
22+cpsize+isize+fsize | msize (variabel) | table | method table, Array variabler Länge von Methoden
jedes Element ist eine method_info-Struktur, definiert in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6 |
… | |||
… | |||
… | |||
22+cpsize+isize+fsize+msize | 2 Bytes | u2 | Attributanzahl, Anzahl der Einträge in der folgenden Attributtabelle |
23+cpsize+isize+fsize+msize | |||
24+cpsize+isize+fsize+msize | asize (variabel) | table | Attributtabelle, Array variabler Länge von Attributen
jedes Element ist eine attribute_info-Struktur, definiert in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7 |
… | |||
… | |||
… |
Darstellung in einer C-ähnlichen ProgrammierspracheBearbeiten
Da C keine Arrays mit mehreren variablen Längen innerhalb einer Struktur unterstützt, lässt sich der folgende Code nicht kompilieren und dient nur zur Demonstration.
struct Class_File_Format { u4 magic_number; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces; u2 fields_count; field_info fields; u2 methods_count; method_info methods; u2 attributes_count; attribute_info attributes;}
Der KonstantenpoolBearbeiten
In der Konstantenpooltabelle werden die meisten literalen konstanten Werte gespeichert. Dazu gehören Werte wie Zahlen aller Art, Zeichenketten, Bezeichnernamen, Verweise auf Klassen und Methoden sowie Typdeskriptoren. Alle Indizes oder Verweise auf bestimmte Konstanten in der Konstanten-Pool-Tabelle werden durch 16-Bit-Zahlen (Typ u2) angegeben, wobei sich der Indexwert 1 auf die erste Konstante in der Tabelle bezieht (der Indexwert 0 ist ungültig).
Aufgrund historischer Entscheidungen, die während der Entwicklung des Dateiformats getroffen wurden, entspricht die Anzahl der Konstanten in der Konstanten-Pool-Tabelle nicht der Anzahl der Konstanten, die der Tabelle vorangestellt ist. Erstens beginnt der Index der Tabelle bei 1 (und nicht bei 0), aber die Anzahl sollte eigentlich als maximaler Index plus 1 interpretiert werden. Außerdem belegen zwei Arten von Konstanten (longs und doubles) zwei aufeinanderfolgende Slots in der Tabelle, obwohl der zweite Slot ein Phantom-Index ist, der nie direkt verwendet wird.
Der Typ jedes Elements (Konstante) im Konstanten-Pool wird durch ein anfängliches Byte-Tag identifiziert. Die Anzahl der Bytes, die diesem Tag folgen, und ihre Interpretation hängen dann von dem Tag-Wert ab. Die gültigen Konstantentypen und ihre Tag-Werte sind:
Tag-Byte | Zusätzliche Bytes | Beschreibung der Konstante | Eingeführte Version |
---|---|---|---|
1 | 2+x Bytes (variabel) |
UTF-8 (Unicode) string: eine Zeichenkette, der eine 16-Bit-Zahl (Typ u2) vorangestellt ist, die die Anzahl der Bytes in der unmittelbar folgenden kodierten Zeichenkette angibt (die von der Anzahl der Zeichen abweichen kann). Beachten Sie, dass die verwendete Kodierung nicht wirklich UTF-8 ist, sondern eine leichte Modifikation der Unicode-Standardkodierungsform darstellt. | 1.0.2 |
3 | 4 Bytes | Ganzzahl: eine vorzeichenbehaftete 32-Bit-Zweierkomplementzahl im Big-Endian-Format | 1.0.2 |
4 | 4 Bytes | Float: eine 32-Bit IEEE 754 Gleitkommazahl mit einfacher Genauigkeit | 1.0.2 |
5 | 8 Bytes | Lang: eine vorzeichenbehaftete 64-Bit-Zweierkomplement-Zahl im Big-Endian-Format (belegt zwei Plätze in der Konstanten-Pool-Tabelle) | 1.0.2 |
6 | 8 Bytes | Double: eine 64-Bit-IEEE-754-Gleitkommazahl mit doppelter Genauigkeit (nimmt zwei Slots in der Konstanten-Pool-Tabelle ein) | 1.0.2 |
7 | 2 Bytes | Klassenreferenz: ein Index innerhalb des Konstantenpools zu einem UTF-8-String, der den voll qualifizierten Klassennamen (im internen Format) enthält (Big-Endian) | 1.0.2 |
8 | 2 Bytes | String-Referenz: ein Index innerhalb des Konstanten-Pools auf einen UTF-8-String (auch Big-Endian) | 1.0.2 |
9 | 4 Bytes | Feldreferenz: zwei Indizes innerhalb des Konstantenpools, der erste zeigt auf eine Klassenreferenz, der zweite auf einen Name und Typ Deskriptor. (big-endian) | 1.0.2 |
10 | 4 bytes | Methodenreferenz: zwei Indizes innerhalb des Konstantenpools, wobei der erste auf eine Klassenreferenz, der zweite auf einen Name- und Typdeskriptor zeigt. (big-endian) | 1.0.2 |
11 | 4 bytes | Methodenreferenz der Schnittstelle: zwei Indizes innerhalb des Konstantenpools, wobei der erste auf eine Klassenreferenz, der zweite auf einen Name- und Typdeskriptor zeigt. (big-endian) | 1.0.2 |
12 | 4 bytes | Name und Typdeskriptor: zwei Indizes auf UTF-8-Strings innerhalb des Konstantenpools, wobei der erste einen Namen (Bezeichner) und der zweite einen speziell kodierten Typdeskriptor darstellt. | 1.0.2 |
15 | 3 Bytes | Methodenhandle: diese Struktur wird verwendet, um ein Methodenhandle darzustellen und besteht aus einem Byte des Typdeskriptors, gefolgt von einem Index innerhalb des Konstantenpools. | 7 |
16 | 2 Bytes | Methodentyp: Diese Struktur wird zur Darstellung eines Methodentyps verwendet und besteht aus einem Index innerhalb des Konstantenpools. | 7 |
17 | 4 Bytes | Dynamisch: Diese Struktur wird verwendet, um eine dynamisch berechnete Konstante anzugeben, die durch den Aufruf einer Bootstrap-Methode erzeugt wird. | 11 |
18 | 4 Bytes | InvokeDynamic: wird von einer invokedynamic-Anweisung verwendet, um eine Bootstrap-Methode, den dynamischen Aufrufnamen, die Argument- und Rückgabetypen des Aufrufs und optional eine Folge zusätzlicher Konstanten, die statische Argumente für die Bootstrap-Methode genannt werden, anzugeben. | 7 |
19 | 2 Bytes | Modul: Dies wird verwendet, um ein Modul zu identifizieren. | 9 |
20 | 2 bytes | Paket: wird verwendet, um ein Paket zu identifizieren, das von einem Modul exportiert oder geöffnet wird. | 9 |
Es gibt nur zwei integrale konstante Typen, integer und long. Andere ganzzahlige Typen, die in der Hochsprache vorkommen, wie boolean, byte und short, müssen als ganzzahlige Konstante dargestellt werden.
Klassennamen in Java werden, wenn sie vollständig qualifiziert sind, traditionell durch Punkte getrennt, wie z.B. „java.lang.Object“. Innerhalb der Low-Level-Klassenreferenzkonstanten erscheint jedoch eine interne Form, die stattdessen Schrägstriche verwendet, wie z. B. „java/lang/Object“.
Die Unicode-Strings sind trotz des Namens „UTF-8-String“ nicht wirklich nach dem Unicode-Standard kodiert, obwohl er ähnlich ist. Es gibt zwei Unterschiede (siehe UTF-8 für eine vollständige Diskussion). Der erste besteht darin, dass der Codepunkt U+0000 als Zwei-Byte-Sequenz C0 80
(in Hexadezimalzeichen) anstelle der Standard-Ein-Byte-Kodierung 00
kodiert wird. Der zweite Unterschied besteht darin, dass zusätzliche Zeichen (außerhalb des BMP bei U+10000 und darüber) mit einer Surrogat-Paar-Konstruktion ähnlich wie bei UTF-16 kodiert werden, anstatt direkt mit UTF-8 kodiert zu werden. In diesem Fall wird jedes der beiden Surrogate separat in UTF-8 kodiert. Zum Beispiel wird U+1D11E als 6-Byte-Sequenz ED A0 B4 ED B4 9E
kodiert, anstatt der korrekten 4-Byte-UTF-8-Kodierung von F0 9D 84 9E
.