Java Modultypen
Named, Automatic und Unnamed Module in Java und deren Zusammenspiel bei Kompilierung und Ausführung.
Das Java Platform Module System (JPMS) kennt drei Arten von Modulen. Nur eine davon ist das „echte" Modul, das Sie selbst schreiben; die anderen beiden existieren, damit die Millionen von JARs aus der Zeit vor Java 9 weiterhin funktionieren. Zu verstehen, welche Art aus einem bestimmten JAR wird — und dass dies ausschließlich davon abhängt, wo Sie es ablegen — ist der Schlüssel zur schmerzfreien Migration. Diese Seite definiert alle drei Arten, zeigt die Zugriffsregeln zwischen ihnen und belegt die Kategorien mit einem ausführbaren Programm.
Named-Module
Ein Named (explizites) Modul ist eines mit einer module-info.class, das auf dem Modulpfad (--module-path / -p) liegt. Es ist das vollwertige Mitglied:
- Es hat einen Namen aus seinem Deskriptor.
- Es liest nur die Module, die es per
requiresangibt. - Es gibt nur die Pakete frei, die es per
exportsdeklariert.
Dies ist das stark gekapseltes Modul, das das Kapitel zur Moduldeklaration beschrieben hat. Alles, was JPMS verspricht — deklarierte Abhängigkeiten, versteckte Interna, frühzeitige Fehlererkennung — gilt für Named-Module.
Automatic-Module
Ein Automatic-Modul ist ein einfaches JAR (ohne module-info), das auf dem Modulpfad abgelegt wird. JPMS hüllt es in ein Modul, damit Named-Module es während der Migration per requires einbinden können — ohne darauf warten zu müssen, dass der Bibliotheksautor einen Deskriptor hinzufügt. Ein Automatic-Modul:
- Erhält einen Namen, der aus dem JAR-Dateinamen abgeleitet wird (z. B.
guava-32.1.jar→guava), sofern das JAR-Manifest nichtAutomatic-Module-Namesetzt. - Exportiert jedes Paket — es hat keine
exports-Direktive, sodass alle Pakete für alle zugänglich sind. - Liest jedes andere Modul, einschließlich des Unnamed-Moduls, und kann damit weiterhin Classpath-JARs sehen.
Es ist eine Brücke: Es erlaubt Ihnen, mit dem Schreiben von Named-Modulen zu beginnen, die von noch nicht modularisierten Bibliotheken abhängen. Der Preis ist, dass es vollständig auf Kapselung verzichtet, und sein automatisch abgeleiteter Name kann sich ändern, wenn das JAR umbenannt wird — weshalb Automatic-Module-Name im Manifest das Verantwortungsbewusste ist, das eine Bibliothek mitliefern sollte.
Das Unnamed-Modul
Das Unnamed-Modul ist der Sammelbehälter für den Classpath. Jede Klasse, die vom Classpath geladen wird, gehört zum Unnamed-Modul ihres Classloaders. Es:
- Hat keinen Namen (
getName()gibtnullzurück,isNamed()istfalse). - Liest jedes andere Modul im System.
- Exportiert alle seine Pakete an andere Unnamed/Automatic-Module.
Doch es gibt eine bewusste Einbahnstraße: Ein Named-Modul kann das Unnamed-Modul nicht per requires einbinden. Sie können es nicht benennen, also können Sie auch nicht von ihm abhängen. Dies ist die Regel, die die Migrationsreihenfolge erzwingt — ein Named-Modul darf nur von anderen Named- oder Automatic-Modulen abhängen, niemals von reinem Classpath-Code.
Die Zugriffsmatrix
Wer wen lesen kann, lässt sich in einer kleinen Tabelle zusammenfassen:
| Von ↓ / Nach → | Named | Automatic | Unnamed |
|---|---|---|---|
| Named | nur bei requires | nur bei requires | niemals |
| Automatic | ja | ja | ja |
| Unnamed | ja | ja | ja |
Die einzige restriktive Zelle — Named-Code kann Unnamed-Code nicht erreichen — erklärt vollständig, warum die Migration von unten nach oben erfolgt (im nächsten Kapitel behandelt).
Ein praktisches Beispiel: Modulart zur Laufzeit bestimmen
Die Module API teilt Ihnen für jede Klasse mit, ob ihr Modul named ist und ob es automatisch synthetisiert wurde. Dieses Programm untersucht drei Referenzen — seine eigene Klasse (Classpath → Unnamed), einen JDK-Typ (Named) und gibt den Boot-Layer aus — um die Kategorien greifbar zu machen.
Was die Ausgabe zeigt:
- Die eigene Klasse des Programms wurde als UNNAMED mit
null-Name klassifiziert, währendjava.util.ListundHttpClientals NAMED eingestuft wurden (java.base,java.net.http). Beim Ausführen vom Classpath ist Ihr Code immer unnamed; das JDK ist immer eine Sammlung von Named-Modulen. Die Art eines Moduls wird durch die Art des Ladens bestimmt, nicht durch irgendetwas in der Klasse selbst. java.base.canRead(self)gabfalsezurück, aberself.canRead(java.base)gabtruezurück. Das ist die Einbahnstraße in Aktion: Das Unnamed-Modul liest alles, aber kein Named-Modul liest das Unnamed-Modul. Diese Asymmetrie ist genau der Grund, warum Named-Code keinrequiresauf Classpath-Code setzen kann.classify()unterschied Automatic von Named durchdescriptor.isAutomatic(). Sie werden hier keintruesehen (nichts wurde als einfaches JAR auf den Modulpfad gelegt), aber die Prüfung ist genau die, mit der Werkzeuge ein Automatic-Modul erkennen — ein echtes Modulobjekt mit einem synthetisierten, vollständig offenen Deskriptor.isExported("java.util")gabtruezurück, aberisExported("jdk.internal.misc")gabfalsezurück, obwohl beide echte Pakete innerhalb vonjava.basesind. Dieexports-Liste eines Named-Moduls ist eine Erlaubnisliste; nicht exportierte (oder nur qualifiziert exportierte) Pakete sind für externen Code unsichtbar, unabhängig davon, ob siepublicsind. Das Unnamed-Modul hingegen exportiert alles, was es enthält.- Für keine dieser Beobachtungen war eine
module-info.javanötig. Die drei Kategorien sind Laufzeitfakten darüber, wie eine Klasse geladen wurde, undgetModule()sowiegetDescriptor()legen sie offen — dieselben Aufrufe, auf die Migrations-Werkzeuge sich stützen, um herauszufinden, womit sie arbeiten.
Warum drei Modularten existieren
Die beiden Kompatibilitätsarten — Automatic und Unnamed — ermöglichen es, dass Java 9+ unmodifizierte Java-8-Anwendungen ausführt. Sie aktivieren starke Kapselung JAR für JAR: Lassen Sie alles auf dem Classpath (alles Unnamed), ändert sich nichts; verschieben Sie eine Bibliothek ohne Deskriptor auf den Modulpfad, wird sie Automatic; fügen Sie eine module-info.java hinzu, wird sie Named. Als Nächstes zeigen Moduldienste den uses/provides-Mechanismus zur Entkopplung von Modulen, und Modulmigration führt ein echtes Projekt durch diese drei Zustände.