Hallo allerseits! Eine Übersetzung des Artikels wurde speziell für Studenten des Java-Entwicklerkurses erstellt .
Wir werden weiterhin darüber sprechen, wie die Java Virtual Machine intern funktioniert. Im
vorherigen Artikel (
Original in Englisch ) haben wir uns mit dem Subsystem zum Laden von Klassen befasst. In diesem Artikel werden wir über die Struktur von Klassendateien sprechen.
Wie wir bereits wissen, wird der gesamte in der Java-Programmiersprache geschriebene Quellcode zunächst mit dem Java-Compiler, der Teil des Java Development Kit ist, in
Bytecode kompiliert. Der Bytecode wird in einer Binärdatei in einer speziellen Klassendatei gespeichert. Dann werden diese Klassendateien dynamisch (falls erforderlich) vom Klassenladeprogramm (ClassLoader) in den Speicher geladen.
Abbildung - Java-Quellcode-KompilierungJede Datei mit der
.java
in mindestens eine
.class
Datei kompiliert. Für jede im Quellcode definierte Klasse, Schnittstelle und jedes Modul wird eine
.class
Datei erstellt. Dies gilt auch für Interfaces und verschachtelte Klassen.
Hinweis - Der Einfachheit halber werden Dateien mit der Erweiterung
.class
als "Klassendateien" bezeichnet.
Schreiben wir ein einfaches Programm.
public class ClassOne{ public static void main(String[] args){ System.out.println("Hello world"); } static class StaticNestedClass{ } } class ClassTwo{ } interface InterfaceOne{ }
Wenn Sie
javac
für diese Datei
javac
werden die folgenden Dateien angezeigt.
ClassOne$StaticNestedClass.class ClassOne.class ClassTwo.class InterfaceOne.class
Wie Sie sehen, wird für jede Klasse und Schnittstelle eine separate Klassendatei erstellt.
Was ist in der Klassendatei?
Die Klassendatei ist im Binärformat. Die darin enthaltenen Informationen werden in der Regel ohne Einrückung zwischen aufeinanderfolgenden Informationen geschrieben. Alle Informationen sind an Byte-Grenzen ausgerichtet. Alle 16-Bit- und 32-Bit-Werte werden mit zwei oder vier aufeinander folgenden 8-Bit-Bytes geschrieben.
Die Klassendatei enthält die folgenden Informationen.
Magische Nummer, Unterschrift . Die ersten vier Bytes jeder Klassendatei sind immer
0xCAFEBABE
. Diese vier Bytes identifizieren die Java-Klassendatei.
Dateiversion. Die nächsten vier Bytes enthalten die Haupt- und Nebenversion der Datei. Zusammen bestimmen diese Zahlen die Version des Klassendateiformats. Wenn die Klassendatei eine Hauptversion von M und eine Nebenversion von m enthält, wird diese Version als M bezeichnet
Jede JVM unterliegt Einschränkungen für die unterstützten Versionen von Klassendateien. Beispielsweise unterstützt Java 11 Hauptversionen von 45 bis 55, Java 12 - von 45 bis 56.
Pool von Konstanten. Eine Tabelle mit Strukturen, die Zeichenfolgenkonstanten, Klassennamen, Schnittstellen, Felder, Methoden und andere Konstanten in der ClassFile-Struktur und ihren Unterstrukturen darstellen. Jedes Konstantenpoolelement beginnt mit einem Einzelbyte-Tag, das den Konstantentyp definiert. Abhängig vom Typ der Konstante können die folgenden Bytes ein unmittelbarer Konstantenwert oder ein Verweis auf ein anderes Element im Pool sein.
Zugriff auf Flags. Eine Liste von Flags, die angeben, dass die Klasse entweder eine öffentliche oder eine private Schnittstelle ist, die letzte Klasse oder nicht. Verschiedene Flags wie
ACC_PUBLIC
,
ACC_FINAL
,
ACC_INTERFACE
,
ACC_ENUM
usw. sind in der Java Virtual Machine Specification beschrieben.
Diese Klasse. Link zum Eintrag im Konstantenpool.
Super klasse. Link zum Eintrag im Konstantenpool.
Schnittstellen Die Anzahl der von der Klasse implementierten Schnittstellen.
Die Anzahl der Felder. Die Anzahl der Felder in der Klasse oder der Schnittstelle.
Felder. Nach der Anzahl der Felder folgt eine Tabelle mit Strukturen variabler Länge. Eine für jedes Feld mit einer Beschreibung des Feldtyps und des Namens (unter Bezugnahme auf den Konstantenpool).
Anzahl der Methoden. Die Anzahl der Methoden in der Klasse oder der Schnittstelle. Diese Zahl enthält nur Methoden, die explizit in der Klasse definiert sind, ohne von Superklassen geerbte Methoden.
Methoden Weiter sind die Methoden selbst. Für jede Methode sind folgende Informationen enthalten: der Methodendeskriptor (Rückgabetyp und Argumentliste), die Anzahl der für die lokalen Variablen der Methode benötigten Wörter, die maximale Anzahl der für den Methodenoperandenstapel benötigten Stapelwörter, die von der Methode erfasste Ausnahmetabelle, Methodenbytecodes und die Tabelle Zeilennummern.
Die Anzahl der Attribute. Die Anzahl der Attribute in dieser Klasse, Schnittstelle oder diesem Modul.
Attribute Der Anzahl der Attribute folgen Tabellen oder Strukturen variabler Länge, die die einzelnen Attribute beschreiben. Beispielsweise gibt es immer ein Attribut "SourceFile". Es enthält den Namen der Quelldatei, aus der die Klassendatei kompiliert wurde.
Obwohl die Klassendatei nicht direkt von Menschen
gelesen werden kann , gibt es im JDK ein Tool namens
javap , das den Inhalt in einem praktischen Format anzeigt.
Schreiben wir ein einfaches Java-Programm wie unten gezeigt.
package bytecode; import java.io.Serializable; public class HelloWorld implements Serializable, Cloneable { public static void main(String[] args) { System.out.println("Hello World"); } }
Kompilieren wir dieses Programm mit
javac
, das die
HelloWorld.class
Datei erstellt, und verwenden
javap
, um die
HelloWorld.class
Datei
javap
.
javap
mit der Option
-v (verbose)
für
javap
das folgende Ergebnis:
Classfile /Users/apersiankite/Documents/code_practice/java_practice/target/classes/bytecode/HelloWorld.class Last modified 02-Jul-2019; size 606 bytes MD5 checksum 6442d93b955c2e249619a1bade6d5b98 Compiled from "HelloWorld.java" public class bytecode.HelloWorld implements java.io.Serializable,java.lang.Cloneable minor version: 0 major version: 55 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #5 // bytecode/HelloWorld super_class: #6 // java/lang/Object interfaces: 2, fields: 0, methods: 2, attributes: 1 Constant pool: #1 = Methodref #6.#22
Hier können Sie sehen, dass die Klasse öffentlich ist und 37 Einträge im Konstantenpool hat. Es gibt ein Attribut (SourceFile unten), die Klasse implementiert zwei Schnittstellen (Serializable, Cloneable), sie hat keine Felder und es gibt zwei Methoden.
Möglicherweise haben Sie bemerkt, dass es nur eine statische Hauptmethode im Quellcode gibt, aber die Klassendatei besagt, dass es zwei Methoden gibt. Denken Sie an den Standardkonstruktor - dies ist ein Konstruktor ohne Argumente, der vom
javac
Compiler hinzugefügt wurde und dessen Bytecode auch in der Ausgabe sichtbar ist. Konstruktoren gelten als Methoden.
Sie können hier mehr über javap lesen.
Tipp : Mit javap können Sie auch feststellen, wie sich Lambdas von anonymen inneren Klassen unterscheiden.